LINQ-запрос к DataTable

814

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

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

Это запрещено. Как мне получить что-то вроде этого?

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

  • 6
    Вы хотите то, что известно как LINQ to DataSet . Эта ссылка приведет вас к первой из серии публикаций, представленных в блоге команды ADO.NET.
  • 3
    Вы можете найти больше примеров LINQ / Lambda от webmingle.blogspot.com/2010_09_01_archive.html
Теги:
linq
datatable
.net-3.5

20 ответов

1056
Лучший ответ

Вы не можете запросить коллекцию строк DataTable, так как DataRowCollection не реализует IEnumerable<T>. Для DataTable необходимо использовать расширение AsEnumerable(). Например:

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

И как говорит Кейт, вам нужно добавить ссылку на System.Data.DataSetExtensions

AsEnumerable() возвращает IEnumerable<DataRow>. Если вам нужно преобразовать IEnumerable<DataRow> в DataTable, используйте расширение CopyToDataTable().

  • 7
    Версия VB: Dim results = From myRow In myDataTable.AsEnumerable _ Где myRow.Field ("RowNo") = 1 _ Выбрать myRow
  • 14
    У меня уже была ссылка на упомянутую dll, но она отсутствовала using System.Data;
Показать ещё 5 комментариев
86
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow
  • 1
    А как насчет выбора нескольких строк, а не только строки 1?
  • 1
    Просто удалите строку "где", и вы получите все строки
Показать ещё 1 комментарий
41

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

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

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

39

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

27
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")                         
                    };
  • 0
    Как я использую имя? Например, MessageBox.Show(name) не определено.
20
//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();
 // 
20

Использование 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();
  • 1
    AsDataView не отображается в Intellisense для меня. Я включил использование System.Data.Linq и использование System.Linq, но все равно он не работает. Вы знаете, что мне не хватает? Заранее спасибо.
  • 0
    @Naomi Это происходит от System.Data.DataSetExtensions .
12

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

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
  • 1
    Поскольку DataTable.Rows не реализует IEnumerable , я не вижу, как этот запрос может скомпилироваться.
  • 0
    @onedaywhen Я только что видел, как это делается в каком-то коде, и он компилируется. Пытаюсь выяснить почему именно сейчас.
Показать ещё 1 комментарий
10

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

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

Надеюсь, что это поможет;

  • 3
    Я предпочитаю « Объединение методов » (как вы сделали здесь), а не « Синтаксис запроса » (в принятом ответе) просто потому, что это базовое предложение where, которое помещается в одну строку и все еще очень читабельно. Каждому свое.
10

Я понимаю, что это было несколько раз ответило, но просто для того, чтобы предложить другой подход, мне нравится использовать метод .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);
  • 4
    Это работает без ссылки на System.Data.DataSetExtensions.
7

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

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

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

Ex. Имя класса DataSet- > 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);

Счастливое кодирование!

5

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

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

Затем, если вы хотите получить определенное значение:

if(results != null) 
    var foo = results["ColName"].ToString()
4
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
  • 0
    Этот ответ так же много вопросов с ним.
1

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

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();
1
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;
1

В моем приложении я обнаружил, что использование LINQ to Datasets с расширением 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);
  • 0
    Я сомневаюсь, что это быстрее, в общих случаях. Это накладные расходы на две сериализации, одну десериализацию и одну операцию синтаксического анализа. Несмотря на это, я понизил голосование, потому что это не является кратким, то есть сериализация / десериализация не проясняет, что цель состоит в том, чтобы фильтровать список.
  • 0
    @an phu, с помощью метода расширения .AsEnumerable создает коллекцию тяжеловесных объектов System.Data.DataRow . Таблица сериализованных и проанализированных данных создает облегченные данные, состоящие только из имен столбцов и значений каждой строки. Когда запрос выполняется, он загружает данные в память, что для большого набора данных может включать обмен. Иногда накладные расходы на несколько операций меньше, чем накладные расходы на копирование больших объемов данных в память и из нее.
1

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

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

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

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

Вы можете заставить его работать элегантно с помощью 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 ссылку

Ещё вопросы

Сообщество Overcoder
Наверх
Меню