LINQ-Abfrage in einer DataTable


1031

Ich versuche, eine LINQ-Abfrage für ein DataTable-Objekt durchzuführen, und stelle bizarrerweise fest, dass das Ausführen solcher Abfragen für DataTables nicht einfach ist. Zum Beispiel:

var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;

Das ist nicht erlaubt. Wie bringe ich so etwas zum Laufen?

Ich bin erstaunt, dass LINQ-Abfragen in DataTables nicht zulässig sind!


3
Weitere LINQ / Lambda-Beispiele finden Sie unter webmingle.blogspot.com/2010_09_01_archive.html

Antworten:


1279

Sie können nicht Abfrage der DataTable‚s Zeilen Sammlung, da DataRowCollectionnicht implementiert IEnumerable<T>. Sie müssen die AsEnumerable()Erweiterung für verwenden DataTable. Wie so:

var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;

Und wie @Keith sagt, müssen Sie einen Verweis auf System.Data.DataSetExtensions hinzufügen

AsEnumerable()kehrt zurück IEnumerable<DataRow>. Wenn Sie konvertieren müssen , IEnumerable<DataRow>um ein DataTable, verwenden Sie die CopyToDataTable()Erweiterung.

Unten ist eine Abfrage mit Lambda-Ausdruck,

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);

8
VB-Version: Dim results = From myRow In myDataTable.AsEnumerable _ Where myRow.Field ("RowNo") = 1 _ Wählen Sie myRow
Jeff

15
Ich hatte bereits einen Verweis auf die erwähnte DLL, fehlte aberusing System.Data;
Luke Duddridge

5
Die VB-Version muss (Of String) zwischen myRow.Field und ("RowNo") einfügen. Dieser Teil sollte lauten: myRow.Field (Of String) ("RowNo") = 1 - Referenz @ Cros-Kommentar.
Yougotiger

8
Diese Lösung ist unnötig kompliziert. Verwenden Sie myDataTable.Rowsstattdessen wie von @JoelFan vorgeschlagen.
Die Verschwörung

10
@Markus Der Grund, warum die Lösung von @ JoelFan funktioniert, myDataTable.Rowsist, dass die myRowVariable explizit in umgewandelt wird DataRow. Wenn es kompiliert wird, wird diese Abfrage in neu geschrieben myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Persönlich finde ich den Anruf zu AsEnumerable()nicht komplizierter als den Anruf zu Cast<DataRow>(). Soweit ich weiß, ist die Leistung dieselbe, es ist also nur eine Frage der Präferenz.
Collin K

129
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow

2
Was ist mit der Auswahl mehrerer Zeilen anstelle von nur Zeile 1?
Adjit

2
Entfernen Sie einfach die "Wo" -Linie und Sie erhalten alle Zeilen
JoelFan

1
Ja, so mache ich das, außer dass ich es durch (int)myRow["RowNo"]das generische Formular ersetze, myRow.Field<int>("RowNo")um nullbare Typen bequemer zu unterstützen.
Jonas

69

Es ist nicht so, dass sie absichtlich nicht in DataTables erlaubt waren, es ist nur so, dass DataTables die IQueryable- und generischen IEnumerable-Konstrukte vorab datiert, für die Linq-Abfragen ausgeführt werden können.

Beide Schnittstellen erfordern eine Sicherheitsüberprüfung des Sortiertyps. DataTables sind nicht stark typisiert. Dies ist der gleiche Grund, warum Benutzer beispielsweise keine Abfrage für eine ArrayList durchführen können.

Damit Linq funktioniert, müssen Sie Ihre Ergebnisse typsicheren Objekten zuordnen und stattdessen abfragen.


49

Wie @ ch00k sagte:

using System.Data; //needed for the extension methods to work

...

var results = 
    from myRow in myDataTable.Rows 
    where myRow.Field<int>("RowNo") == 1 
    select myRow; //select the thing you want, not the collection

Sie müssen auch einen Projektverweis hinzufügen System.Data.DataSetExtensions


1
Wenn Sie dies versuchen, werden Sie feststellen , es wird nicht funktionieren , wenn Sie eine bestimmte Art setzen auf myRowoder Verwendung Cast<DataRow>()auf Rows. Besser zu benutzen AsEnumerable().
NetMage

1
@ NetMage das hat vor 12 Jahren funktioniert, als ich es gepostet habe. Solange Sie eine aufzählbare Sammlung von ohnehin haben System.Linqund System.Data.DataSetExtensionsdann zurückgeben . Das könnte sich geändert haben, es ist ein Jahrzehnt her, seit ich es benutzt habe. myDataTable.RowsDataRow
Keith

1
Interessant - ich denke, es wurde irgendwann geändert, da es jetzt nicht auf .Net oder .Net Core funktioniert.
NetMage

1
@NetMage Ja, ich bin nicht überrascht, dass die DataSetErweiterungen es nicht in .NET Core oder .NET Standard geschafft haben. Sie waren bereits veraltet, als ich diese Antwort veröffentlichte. Ich würde es wirklich nicht DataSetin neuen Projekten verwenden, es gibt weitaus bessere Datenzugriffsmodelle, sowohl für die einfache Codierung als auch für die Leistung.
Keith

1
Sie sind da, DataRowCollectionimplementieren aber nicht IEnumerable<T>nur IEnumerableund funktionieren daher nicht mit stark typisiertem LINQ.
NetMage

39
var query = from p in dt.AsEnumerable()
                    where p.Field<string>("code") == this.txtCat.Text
                    select new
                    {
                        name = p.Field<string>("name"),
                        age= p.Field<int>("age")                         
                    };

Die Felder Name und Alter sind jetzt Teil des Abfrageobjekts und können wie folgt aufgerufen werden: Console.WriteLine (query.name);


Wie benutze ich den Namen? Zum Beispiel MessageBox.Show(name)ist undefiniert.

35

Mir ist klar, dass dies einige Male beantwortet wurde, aber nur um einen anderen Ansatz anzubieten:

Ich benutze die .Cast<T>()Methode gerne , sie hilft mir dabei, den expliziten Typ zu definieren, der tief definiert ist, und ich denke, er .AsEnumerable()nennt ihn sowieso:

var results = from myRow in myDataTable.Rows.Cast<DataRow>() 
                  where myRow.Field<int>("RowNo") == 1 select myRow;

oder

var results = myDataTable.Rows.Cast<DataRow>()
                      .FirstOrDefault(x => x.Field<int>("RowNo") == 1);

Wie in den Kommentaren erwähnt, werden keine weiteren Baugruppen benötigt, da diese Teil von Linq ( Referenz ) sind.


5
Dies funktioniert ohne Verweis auf System.Data.DataSetExtensions.
user423430

29

Verwenden von LINQ zum Bearbeiten von Daten in DataSet / DataTable

var results = from myRow in tblCurrentStock.AsEnumerable()
              where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
              select myRow;
DataView view = results.AsDataView();

1
Die AsDataView wird für mich nicht in Intellisense angezeigt. Ich habe System.Data.Linq und System.Linq verwendet, aber es funktioniert immer noch nicht. Weißt du was ich vermisse? Danke im Voraus.
Naomi

@ Naomi Es kommt von System.Data.DataSetExtensions.
Louis Waweru

29
//Create DataTable 
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
   new DataColumn("ID",typeof(System.Int32)),
   new DataColumn("Name",typeof(System.String))

});

//Fill with data

dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});

//Now  Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method  i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>


// Now Query DataTable to find Row whoes ID=1

DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
 // 

22

Versuchen Sie diese einfache Abfragezeile:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);

4
Ich bevorzuge die " Methodenverkettung " (wie Sie hier gemacht haben) gegenüber der " Abfragesyntax " (in der akzeptierten Antwort), einfach weil dies eine grundlegende where-Klausel ist, die in eine Zeile passt und immer noch gut lesbar ist. Jedem das Seine.
MikeTeeVee

16

Sie können LINQ für Objekte in der Rows-Auflistung verwenden, z. B.:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;

1
Da DataTable.Rowsnicht implementiert IEnumerable, kann ich nicht sehen, wie diese Abfrage kompiliert werden könnte.
Tag, wenn

@onedaywhen ich gerade gesehen habe, dass dies in einem Code gemacht wird und es kompiliert. Ich versuche gerade herauszufinden, warum.
BVernon

... oder Sie können einfach einen Filterausdruck innerhalb der Select-Methode verwenden: var results = myDataTable.Select ("RowNo = 1"); Dies gibt ein DataRow-Array zurück.
Ishikawa

12

Dies ist eine einfache Methode, die für mich funktioniert und Lambda-Ausdrücke verwendet:

var results = myDataTable.Select("").FirstOrDefault(x => (int)x["RowNo"] == 1)

Wenn Sie dann einen bestimmten Wert wünschen:

if(results != null) 
    var foo = results["ColName"].ToString()

11

Versuche dies

var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ; 

11

Höchstwahrscheinlich sind die Klassen für DataSet, DataTable und DataRow bereits in der Lösung definiert. In diesem Fall benötigen Sie die DataSetExtensions-Referenz nicht.

Ex. DataSet-Klassenname-> CustomSet, DataRow-Klassenname-> CustomTableRow (mit definierten Spalten: RowNo, ...)

var result = from myRow in myDataTable.Rows.OfType<CustomSet.CustomTableRow>()
             where myRow.RowNo == 1
             select myRow;

Oder (wie ich es vorziehe)

var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);

9
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;

Diese Antwort als viele Probleme damit.
Herr Anderson

8

In meiner Anwendung stellte ich fest, dass die Verwendung von LINQ für Datensätze mit der Erweiterung AsEnumerable () für DataTable, wie in der Antwort vorgeschlagen, äußerst langsam war. Wenn Sie an einer Geschwindigkeitsoptimierung interessiert sind, verwenden Sie die Json.Net-Bibliothek von James Newtonking ( http://james.newtonking.com/json/help/index.html ).

// Serialize the DataTable to a json string
string serializedTable = JsonConvert.SerializeObject(myDataTable);    
Jarray dataRows = Jarray.Parse(serializedTable);

// Run the LINQ query
List<JToken> results = (from row in dataRows
                    where (int) row["ans_key"] == 42
                    select row).ToList();

// If you need the results to be in a DataTable
string jsonResults = JsonConvert.SerializeObject(results);
DataTable resultsTable = JsonConvert.DeserializeObject<DataTable>(jsonResults);

Ich bezweifle, dass dies im Allgemeinen schneller ist. Es hat den Overhead von zwei Serialisierungs-, einer Deserialisierungs- und einer Parsing-Operation. Unabhängig davon habe ich abgestimmt, weil es nicht präzise ist, dh die Serialisierung / Deserialisierung macht nicht klar, dass die Absicht darin besteht, eine Liste zu filtern.
ein Phu

@an phu erstellt mit der Erweiterungsmethode .AsEnumerable eine Sammlung schwerer System.Data.DataRowObjekte. Die serialisierte und analysierte Datentabelle erstellt kompakte Daten, die nur aus den Spaltennamen und Werten jeder Zeile bestehen. Wenn die Abfrage ausgeführt wird, werden die Daten in den Speicher geladen, was bei einem großen Dataset möglicherweise einen Austausch erfordert. Manchmal ist der Aufwand für mehrere Vorgänge geringer als der Aufwand für das Kopieren großer Datenmengen in den und aus dem Speicher.
LandedGently

7

Für VB.NET Der Code sieht folgendermaßen aus:

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow

7
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;

7

Ein Beispiel dafür, wie dies erreicht werden kann, finden Sie unten:

DataSet dataSet = new DataSet(); //Create a dataset
dataSet = _DataEntryDataLayer.ReadResults(); //Call to the dataLayer to return the data

//LINQ query on a DataTable
var dataList = dataSet.Tables["DataTable"]
              .AsEnumerable()
              .Select(i => new
              {
                 ID = i["ID"],
                 Name = i["Name"]
               }).ToList();

6

Versuche dies...

SqlCommand cmd = new SqlCommand( "Select * from Employee",con);
SqlDataReader dr = cmd.ExecuteReader( );
DataTable dt = new DataTable( "Employee" );
dt.Load( dr );
var Data = dt.AsEnumerable( );
var names = from emp in Data select emp.Field<String>( dt.Columns[1] );
foreach( var name in names )
{
    Console.WriteLine( name );
}

5

Sie können es elegant über linq wie folgt arbeiten lassen:

from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod

Oder wie bei Dynamic Linq (AsDynamic wird direkt in DataSet aufgerufen):

TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)

Ich bevorzuge den letzten Ansatz, während er am flexibelsten ist. PS: Vergessen Sie nicht, die System.Data.DataSetExtensions.dllReferenz anzuschließen


5

Sie können dies versuchen, aber Sie müssen sicher sein, welche Art von Werten für jede Spalte gilt

List<MyClass> result = myDataTable.AsEnumerable().Select(x=> new MyClass(){
     Property1 = (string)x.Field<string>("ColumnName1"),
     Property2 = (int)x.Field<int>("ColumnName2"),
     Property3 = (bool)x.Field<bool>("ColumnName3"),    
});

Ist die Welt verrückt geworden? Was ist los mit SQL? DataRow [] drs = dt.Select ("id = 1"); Vielleicht ist das zu einfach.
Programnik

0

Ich schlage folgende Lösung vor:

DataView view = new DataView(myDataTable); 
view.RowFilter = "RowNo = 1";
DataTable results = view.ToTable(true);

In der DataView-Dokumentation sehen wir zunächst Folgendes :

Stellt eine datenindigierbare, benutzerdefinierte Ansicht einer DataTable zum Sortieren, Filtern, Suchen, Bearbeiten und Navigieren dar.

Daraus ergibt sich, dass DataTable nur Daten speichern soll und DataView es uns ermöglicht, die DataTable abzufragen.

So funktioniert das in diesem speziellen Fall:

Sie versuchen, die SQL-Anweisung zu implementieren

SELECT *
FROM myDataTable
WHERE RowNo = 1

in "DataTable-Sprache". In C # würden wir es so lesen:

FROM myDataTable
WHERE RowNo = 1
SELECT *

was in C # so aussieht:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *

0
                    //Json Formating code
                    //DT is DataTable
                    var filter = (from r1 in DT.AsEnumerable()

                                  //Grouping by multiple columns 
                                  group r1 by new
                                  {
                                      EMPID = r1.Field<string>("EMPID"),
                                      EMPNAME = r1.Field<string>("EMPNAME"),

                                  } into g
                                  //Selecting as new type
                                  select new
                                  {

                                      EMPID = g.Key.EMPID,
                                      MiddleName = g.Key.EMPNAME});
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.