Запрос LINQ для DataTable

Я пытаюсь выполнить запрос LINQ для объекта DataTable, и странно, что я нахожу, что выполнение таких запросов на DataTables не является простым. Например:

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

это запрещено. Как заставить что-то вроде этого работать?

Я поражен, что запросы LINQ не разрешены в DataTables!

21 ответов


вы не можете запросить против DataTable ' s строки сбор, начиная с DataRowCollection не выполнять IEnumerable<T>. Вам нужно использовать


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

дело не в том, что они были намеренно запрещены в DataTables, просто DataTables предшествуют IQueryable и generic IEnumerable конструкциям, на которых могут выполняться запросы Linq.

оба интерфейса требуют некоторой проверки типа безопасности. DataTables не строго типизированы. Это та же причина, по которой люди не могут запрашивать, например, ArrayList.

для работы Linq вам нужно сопоставить свои результаты с типобезопасными объектами и запросить вместо этого.


Как @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

вам также необходимо добавить ссылку на проект в System.Data.DataSetExtensions


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

использование LINQ для управления данными в 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();

//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();
 // 

Я понимаю, что на это ответили несколько раз, но просто предложить другой подход, мне нравится использовать .Cast<T>() метод, это помогает мне поддерживать здравомыслие в том, чтобы видеть явный тип, определенный, и глубоко внутри я думаю .AsEnumerable() называет это так:

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

или

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

вы можете использовать LINQ для объектов в коллекции строк, например:

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

попробуйте эту простую строку запрос:

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

попробуй такое

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

Это простой способ, который работает для меня и использует лямбда-выражения:

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

тогда, если вы хотите определенное значение:

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

скорее всего, классы для DataSet, DataTable и DataRow уже определены в решении. Если это так, вам не понадобится ссылка DataSetExtensions.

Ex. Имя класса набора данных - > CustomSet, имя класса DataRow - > CustomTableRow (с определенными столбцами: RowNo,...)

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

или (как я предпочитаю)

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

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

в моем приложении я обнаружил, что использование LINQ для наборов данных с расширением AsEnumerable() для DataTable, как предложено в ответе, было очень медленным. Если вы заинтересованы в оптимизации скорости, используйте James Newtonking Json.Net библиотека (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);

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

вы можете заставить его работать элегантно через linq следующим образом:

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

или как динамический linq это (AsDynamic вызывается непосредственно на DataSet):

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

Я предпочитаю последний подход, в то время как это является наиболее гибким. P. S.: Не забудьте подключить System.Data.DataSetExtensions.dll ссылка


для VB.NET код будет выглядеть так:

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

пример о том, как достичь этого, представленная ниже:

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();

попробуйте это...

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

вы можете попробовать это, но вы должны убедиться, что тип значений для каждого столбца

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