Consulta LINQ en una DataTable


1031

Estoy tratando de realizar una consulta LINQ en un objeto DataTable y extrañamente encuentro que realizar tales consultas en DataTables no es sencillo. Por ejemplo:

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

Esto no esta permitido. ¿Cómo hago para que algo así funcione?

¡Estoy sorprendido de que las consultas LINQ no estén permitidas en DataTables!


3
Puede encontrar más ejemplos de LINQ / Lambda en webmingle.blogspot.com/2010_09_01_archive.html

Respuestas:


1279

No puede consultar la colección de filas deDataTable 's , ya que no se implementa . Necesita usar la extensión para . Al igual que:DataRowCollectionIEnumerable<T>AsEnumerable()DataTable

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

Y como dice @Keith , deberá agregar una referencia a System.Data.DataSetExtensions

AsEnumerable()vuelve IEnumerable<DataRow>. Si necesita convertir IEnumerable<DataRow>a a DataTable, use la CopyToDataTable()extensión.

A continuación se muestra la consulta con la expresión Lambda,

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

8
Versión VB: resultados tenues = desde myRow en myDataTable.AsEnumerable _ Donde myRow.Field ("RowNo") = 1 _ Seleccione myRow
Jeff el

15
Ya tenía una referencia al dll mencionado, pero me faltabausing System.Data;
Luke Duddridge,

55
La versión VB necesita insertar (Of String) entre myRow.Field y ("RowNo"). Esa parte debería leer: myRow.Field (Of String) ("RowNo") = 1 - Comentario @Cros comentario.
yougotiger

8
Esta solución es innecesariamente complicada. Use en su myDataTable.Rowslugar como sugirió @JoelFan.
The Conspiracy

10
@Markus Solo para aclarar, la razón por la cual la solución de @ JoelFan funciona myDataTable.Rowses porque la myRowvariable está explícitamente convertida en DataRow. Cuando se compila, esa consulta se reescribe en myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Personalmente, no encuentro la llamada a AsEnumerable()más complicada que la llamada a Cast<DataRow>(). Hasta donde yo sé, el rendimiento es el mismo, por lo que es solo una cuestión de preferencia.
Collin K

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

2
¿Qué pasa para seleccionar varias filas, en lugar de solo la fila 1?
Adjit

2
Simplemente elimine la línea "donde" y obtendrá todas las filas
JoelFan

1
Sí, así es como lo hago, excepto para reemplazar (int)myRow["RowNo"]con la forma genérica myRow.Field<int>("RowNo")para admitir más convenientemente los tipos anulables.
Jonas

69

No es que no se les haya permitido deliberadamente en DataTables, es solo que DataTables es anterior a las construcciones IQueryable y genéricas IEnumerable en las que se pueden realizar consultas Linq.

Ambas interfaces requieren algún tipo de validación de seguridad de tipo. Las tablas de datos no están fuertemente tipadas. Esta es la misma razón por la cual las personas no pueden consultar contra una ArrayList, por ejemplo.

Para que Linq funcione, necesita mapear sus resultados con objetos seguros para escribir y consultar en su lugar.


49

Como @ ch00k dijo:

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

También debe agregar una referencia de proyecto a System.Data.DataSetExtensions


1
Si intenta esto, usted encontrará que no funcionará a menos que poner un tipo específico sobre myRowo uso Cast<DataRow>()sobre Rows. Mejor de usar AsEnumerable().
NetMage

1
@NetMage funcionó hace 12 años cuando lo publiqué. Siempre que tenga System.Linqy System.Data.DataSetExtensionsluego myDataTable.Rowsdevuelva una colección enumerable de DataRowtodos modos. Eso podría haber cambiado, ha pasado una década desde que lo usé.
Keith

1
Interesante: supongo que se cambió en algún momento, ya que ahora no funciona en .Net o .Net Core.
NetMage

1
@NetMage sí, no me sorprende que las DataSetextensiones no hayan llegado a .NET Core o .NET Standard, ya estaban desactualizadas cuando publiqué esta respuesta. Realmente no lo usaría DataSeten nuevos proyectos, hay modelos de acceso a datos mucho mejores, tanto para facilitar la codificación como para el rendimiento.
Keith

1
Están allí, pero DataRowCollectionno se implementa IEnumerable<T>solo IEnumerabley, por lo tanto, no funciona con LINQ fuertemente tipado.
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")                         
                    };

los campos de nombre y edad ahora son parte del objeto de consulta y se puede acceder de esta manera: Console.WriteLine (query.name);


¿Cómo uso el nombre? Por ejemplo, MessageBox.Show(name)no está definido.

35

Me doy cuenta de que esto ha sido respondido varias veces, pero solo para ofrecer otro enfoque:

Me gusta usar el .Cast<T>()método, me ayuda a mantener la cordura al ver el tipo explícito definido y en el fondo creo que lo .AsEnumerable()llama de todos modos:

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

o

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

Como se señaló en los comentarios, no se necesitan otros ensamblajes ya que es parte de Linq ( Referencia )


55
Esto funciona sin hacer referencia a System.Data.DataSetExtensions.
user423430

29

Usando LINQ para manipular datos en 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
AsDataView no aparece en Intellisense para mí. Incluí usar System.Data.Linq y System.Linq pero aún no funciona. ¿Sabes lo que me estoy perdiendo? Gracias por adelantado.
Naomi

@Naomi Viene de 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

Pruebe esta simple línea de consulta:

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

44
Prefiero el " Método de encadenamiento " (como lo ha hecho aquí) sobre la " Sintaxis de consulta " (en la respuesta aceptada) simplemente porque esta es una cláusula where básica que cabe en una línea y aún es muy legible. A cada cual lo suyo.
MikeTeeVee

16

Puede usar LINQ para objetos en la colección Filas, de esta manera:

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

1
Como DataTable.Rowsno se implementa IEnumerable, no puedo ver cómo se podría compilar esta consulta.
cuando el

@onedaycuando acabo de ver que esto se hace en algún código y se compila. Tratando de entender por qué en este momento.
BVernon

... o simplemente puede usar una expresión de filtro dentro del método Select: var results = myDataTable.Select ("RowNo = 1"); Esto devuelve una matriz DataRow.
Ishikawa

12

Esta es una manera simple que funciona para mí y utiliza expresiones lambda:

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

Entonces, si quieres un valor particular:

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

11

Prueba esto

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

11

Lo más probable es que las clases para DataSet, DataTable y DataRow ya estén definidas en la solución. Si ese es el caso, no necesitará la referencia DataSetExtensions.

Ex. Nombre de clase DataSet-> CustomSet, nombre de clase DataRow-> CustomTableRow (con columnas definidas: RowNo, ...)

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

O (como prefiero)

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;

Esta respuesta tiene muchos problemas.
Sr. Anderson

8

En mi aplicación, descubrí que usar LINQ to Datasets con la extensión AsEnumerable () para DataTable, como se sugiere en la respuesta, era extremadamente lento. Si está interesado en optimizar la velocidad, use la biblioteca 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);

Dudo que esto sea más rápido, en los casos generales. Tiene la sobrecarga de dos operaciones de serialización, una deserialización y una de análisis. De todos modos, voté en contra porque no es conciso, es decir, la serialización / deserialización no deja en claro que la intención es filtrar una lista.
un phu

@an phu, utilizando el método de extensión .AsEnumerable crea una colección de System.Data.DataRowobjetos pesados . La tabla de datos serializados y analizados crea datos livianos que consisten solo en los nombres y valores de columna de cada fila. Cuando se ejecuta la consulta, cargará los datos en la memoria, lo que para un gran conjunto de datos puede implicar el intercambio. A veces, la sobrecarga de varias operaciones es menor que la sobrecarga de copiar grandes cantidades de datos dentro y fuera de la memoria.
LandedGently

7

Para VB.NET El código se verá así:

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

Ejemplo sobre cómo lograr esto se proporciona a continuación:

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

Prueba esto...

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

Puede hacerlo funcionar elegante a través de linq de esta manera:

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

O como dynamic linq this (AsDynamic se llama directamente en DataSet):

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

Prefiero el último enfoque, mientras que es el más flexible. PD: no olvides conectar la System.Data.DataSetExtensions.dllreferencia


5

puede intentar esto, pero debe estar seguro del tipo de valores para cada columna

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"),    
});

¿Se ha vuelto loco el mundo? ¿Qué pasa con sql? DataRow [] drs = dt.Select ("id = 1"); Quizás esto es demasiado fácil.
Programnik

0

Propongo la siguiente solución:

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

Mirando la documentación de DataView , lo primero que podemos ver es esto:

Representa una vista personalizada de datos enlazables de una tabla de datos para ordenar, filtrar, buscar, editar y navegar.

Lo que obtengo de esto es que DataTable está destinado a almacenar solo datos y DataView está allí nos permite "consultar" contra DataTable.

Así es como funciona esto en este caso particular:

Intenta implementar la instrucción SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

en "lenguaje DataTable". En C # lo leeríamos así:

FROM myDataTable
WHERE RowNo = 1
SELECT *

que se ve en C # así:

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});
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.