Requête LINQ sur un DataTable

1031

J'essaie d'effectuer une requête LINQ sur un objet DataTable et bizarrement, je trouve que l'exécution de telles requêtes sur DataTables n'est pas simple. Par exemple:

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

Ce n'est pas permis. Comment faire fonctionner quelque chose comme ça?

Je suis étonné que les requêtes LINQ ne soient pas autorisées sur DataTables!

Calanus
la source
3
Vous pouvez trouver plus d'exemple LINQ / Lambda sur webmingle.blogspot.com/2010_09_01_archive.html

Réponses:

1279

Vous ne pouvez pas la requête contre DataTablel » Rows collection, car DataRowCollectionne met pas en œuvre IEnumerable<T>. Vous devez utiliser l' AsEnumerable()extension pour DataTable. Ainsi:

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

Et comme le dit @Keith , vous devrez ajouter une référence à System.Data.DataSetExtensions

AsEnumerable()retourne IEnumerable<DataRow>. Si vous devez convertir IEnumerable<DataRow>en un DataTable, utilisez l' CopyToDataTable()extension.

Ci-dessous est une requête avec Lambda Expression,

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);
Collin K
la source
8
Version VB: résultats faibles = de myRow dans myDataTable.AsEnumerable _ Où myRow.Field ("RowNo") = 1 _ Sélectionnez myRow
Jeff
15
J'avais déjà une référence à la DLL mentionnée, mais il manquaitusing System.Data;
Luke Duddridge
5
La version VB doit insérer (Of String) entre myRow.Field et ("RowNo"). Cette partie doit se lire: myRow.Field (Of String) ("RowNo") = 1 - Référence @Cros commentaire.
yougotiger
8
cette solution est inutilement compliquée. Utilisez myDataTable.Rowsplutôt comme l'a suggéré @JoelFan.
The Conspiracy
10
@Markus Juste pour clarifier, la raison pour laquelle la solution de @ JoelFan fonctionne myDataTable.Rowsest parce que la myRowvariable est explicitement transtypée en DataRow. Une fois compilée, cette requête est réécrite myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Personnellement, je ne trouve pas l'appel à AsEnumerable()plus compliqué que l'appel à Cast<DataRow>(). Pour autant que je sache, les performances sont les mêmes, donc c'est juste une question de préférence.
Collin K
129
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow
JoelFan
la source
2
Qu'en est-il de la sélection de plusieurs lignes, au lieu de simplement la ligne 1?
Adjit le
2
Supprimez simplement la ligne "où" et vous obtiendrez toutes les lignes
JoelFan
1
Oui, c'est comme ça que je l'utilise, sauf pour le remplacer (int)myRow["RowNo"]par le formulaire générique myRow.Field<int>("RowNo")pour prendre en charge plus facilement les types nullables.
Jonas
69

Ce n'est pas qu'ils n'étaient délibérément pas autorisés sur DataTables, c'est juste que DataTables est antérieur aux constructions IQueryable et IEnumerable génériques sur lesquelles les requêtes Linq peuvent être effectuées.

Les deux interfaces nécessitent une sorte de validation de sécurité de type. Les DataTables ne sont pas fortement typées. C'est la même raison pour laquelle les gens ne peuvent pas interroger une ArrayList, par exemple.

Pour que Linq fonctionne, vous devez mapper vos résultats par rapport à des objets de type sécurisé et les interroger à la place.

Jon Limjap
la source
49

Comme l'a dit @ ch00k:

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

Vous devez également ajouter une référence de projet à System.Data.DataSetExtensions

Keith
la source
1
Si vous essayez cela, vous constaterez que cela ne fonctionnera que si vous mettez un type spécifique myRowou utilisez- Cast<DataRow>()le Rows. Mieux à utiliser AsEnumerable().
NetMage
1
@NetMage, cela a fonctionné il y a 12 ans lorsque je l'ai publié. Tant que vous avez System.Linq, System.Data.DataSetExtensionspuis myDataTable.Rowsretournez une collection énumérable de DataRowtoute façon. Cela a peut-être changé, cela fait une décennie que je ne l'ai pas utilisé.
Keith
1
Intéressant - je suppose qu'il a été modifié à un moment donné, car il ne fonctionne pas sur .Net ou .Net Core maintenant.
NetMage
1
@NetMage oui, je ne suis pas surpris que les DataSetextensions ne soient pas entrées dans .NET Core ou .NET Standard, elles étaient déjà obsolètes lorsque j'ai posté cette réponse. Je ne l'utiliserais vraiment pas DataSetdans de nouveaux projets, il existe de bien meilleurs modèles d'accès aux données, à la fois pour faciliter le codage et les performances.
Keith
1
Ils sont là, mais DataRowCollectionne sont pas implémentés IEnumerable<T>uniquement IEnumerableet ne fonctionnent donc pas avec LINQ fortement typé.
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")                         
                    };

les champs de nom et d'âge font maintenant partie de l'objet de requête et sont accessibles comme suit : Console.WriteLine (query.name);

Ravi
la source
Comment j'utilise le nom? Par exemple, MessageBox.Show(name)n'est pas défini.
35

Je me rends compte que cela a été répondu plusieurs fois, mais juste pour proposer une autre approche:

J'aime utiliser la .Cast<T>()méthode, cela m'aide à rester sain d'esprit en voyant le type explicite défini et au fond, je pense .AsEnumerable()qu'il l'appelle de toute façon:

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

ou

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

Comme indiqué dans les commentaires, aucun autre assemblage n'est nécessaire car il fait partie de Linq ( référence )

vandsh
la source
5
Cela fonctionne sans référencer System.Data.DataSetExtensions.
user423430
29

Utilisation de LINQ pour manipuler des données dans 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();
Salim
la source
1
L'AsDataView n'apparaît pas dans Intellisense pour moi. J'ai inclus l'utilisation de System.Data.Linq et l'utilisation de System.Linq mais cela ne fonctionne toujours pas. Savez-vous ce qui me manque? Merci d'avance.
Naomi
@Naomi Ça vient 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();
 // 
sushil pandey
la source
22

Essayez cette simple ligne de requête:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
Mohit Verma
la source
4
Je préfère le " Method Chaining " (comme vous l'avez fait ici) à la " Query Syntax " (dans la réponse acceptée) simplement parce que c'est une clause where de base qui tient sur une seule ligne et qui est toujours très lisible. À chacun son bonheur.
MikeTeeVee
16

Vous pouvez utiliser LINQ pour des objets de la collection Rows, comme ceci:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
David Wengier
la source
1
Parce que DataTable.Rowsn'implémente pas IEnumerable, je ne vois pas comment cette requête pourrait être compilée.
onedaywhen
@onedaywhen je viens de voir que cela se fait dans du code et qu'il se compile. Essayer de comprendre pourquoi maintenant.
BVernon
... ou vous pouvez simplement utiliser une expression de filtre dans la méthode Select: var results = myDataTable.Select ("RowNo = 1"); Cela renvoie un tableau DataRow.
Ishikawa
12

C'est un moyen simple qui fonctionne pour moi et utilise des expressions lambda:

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

Ensuite, si vous voulez une valeur particulière:

if(results != null) 
    var foo = results["ColName"].ToString()
Matt Kemp
la source
11

Essaye ça

var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ; 
midhun sankar
la source
11

Très probablement, les classes pour DataSet, DataTable et DataRow sont déjà définies dans la solution. Si tel est le cas, vous n'aurez pas besoin de la référence DataSetExtensions.

Ex. Nom de la classe DataSet-> CustomSet, nom de la classe DataRow-> CustomTableRow (avec les colonnes définies: RowNo, ...)

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

Ou (comme je préfère)

var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);
xadriel
la source
9
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
Vinay
la source
Cette réponse comme beaucoup de problèmes avec elle.
M. Anderson
8

Dans mon application, j'ai trouvé que l'utilisation de LINQ to Datasets avec l'extension AsEnumerable () pour DataTable comme suggéré dans la réponse était extrêmement lente. Si vous souhaitez optimiser la vitesse, utilisez la bibliothèque Json.Net de 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);
Atterri doucement
la source
Je doute que ce soit plus rapide, dans les cas généraux. Il a la surcharge de deux opérations de sérialisation, une désérialisation et une analyse. Quoi qu'il en soit, j'ai rétrogradé car ce n'est pas concis, c'est-à-dire que la sérialisation / désérialisation ne précise pas que l'intention est de filtrer une liste.
un phu
@an phu, l'utilisation de la méthode d'extension .AsEnumerable crée une collection d' System.Data.DataRowobjets lourds . Le tableau de données sérialisé et analysé crée des données légères composées uniquement des noms de colonne et des valeurs de chaque ligne. Lorsque la requête s'exécute, elle charge les données en mémoire, ce qui peut impliquer un échange pour un grand ensemble de données. Parfois, la surcharge de plusieurs opérations est inférieure à celle de la copie de grandes quantités de données dans et hors de la mémoire.
LandedGently
7

Pour VB.NET Le code ressemblera à ceci:

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
Abdul Saboor
la source
7
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;
Iman
la source
7

Exemple sur la façon d'y parvenir fourni ci-dessous:

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();
Ryan Gavin
la source
6

Essaye ça...

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 );
}
Uthaiah
la source
5

Vous pouvez le faire fonctionner élégamment via linq comme ceci:

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

Ou comme linq dynamique ceci (AsDynamic est appelé directement sur DataSet):

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

Je préfère la dernière approche alors qu'elle est la plus flexible. PS: N'oubliez pas de connecter la System.Data.DataSetExtensions.dllréférence

AuthorProxy
la source
5

vous pouvez essayer cela, mais vous devez être sûr du type de valeurs pour chaque colonne

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"),    
});
Gabriel Martinez Bustos
la source
Le monde est-il devenu fou? Quel est le problème avec sql? DataRow [] drs = dt.Select ("id = 1"); C'est peut-être trop facile.
Programnik
0

Je propose la solution suivante:

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

En regardant la documentation DataView , la première chose que nous pouvons voir est la suivante:

Représente une vue personnalisée et databindable d'un DataTable pour le tri, le filtrage, la recherche, la modification et la navigation.

Ce que je retiens de cela, c'est que DataTable est destiné à stocker uniquement des données et DataView est là nous permet de "requête" contre le DataTable.

Voici comment cela fonctionne dans ce cas particulier:

Vous essayez d'implémenter l'instruction SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

dans "Langage DataTable". En C #, nous le lirions comme ceci:

FROM myDataTable
WHERE RowNo = 1
SELECT *

qui ressemble en C # à ceci:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *
Alan
la source
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});
chandra rv
la source