LINQ-запрос к DataTable

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

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

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

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

14.08.2008 10:08:27
Вы можете найти больше примеров LINQ / Lambda от webmingle.blogspot.com/2010_09_01_archive.html
user562221 17.02.2011 19:18:59
23 ОТВЕТА
РЕШЕНИЕ

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

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

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

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

Ниже приведен запрос с лямбда-выражением,

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);
1273
6.04.2020 09:22:00
Версия VB: Dim results = From myRow В myDataTable.AsEnumerable _ Где myRow.Field ("RowNo") = 1 _ Выбрать myRow
Jeff 29.07.2009 20:46:28
У меня уже была ссылка на упомянутую dll, но она пропалаusing System.Data;
Luke Duddridge 31.05.2011 10:37:17
Версия VB необходимо вставить (Of String) между myRow.Field и ("RowNo"). Эта часть должна выглядеть следующим образом: myRow.Field (Of String) ("RowNo") = 1 - ссылка @Cros комментарий.
yougotiger 18.06.2012 22:26:27
это решение излишне сложно. Используйте myDataTable.Rowsвместо этого, как предложил @JoelFan.
The Conspiracy 25.06.2014 18:46:35
@Markus Просто чтобы уточнить, причина, по которой работает решение @ JoelFan, myDataTable.Rowsзаключается в том, что myRowпеременная явно приведена к DataRow. Когда он компилируется, этот запрос переписывается в myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Лично я не считаю вызов AsEnumerable()более сложным, чем вызов Cast<DataRow>(). Насколько я знаю, производительность одинакова, так что это просто вопрос предпочтений.
Collin K 26.06.2014 16:29:21

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

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

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

69
14.08.2008 10:10:55

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

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
16
14.08.2008 10:11:08
Потому DataTable.Rowsчто не реализует IEnumerable, я не вижу, как этот запрос может скомпилироваться.
onedaywhen 11.07.2016 08:45:02
@onedaywhen Я только что видел, как это делается в каком-то коде, и он компилируется. Пытаюсь выяснить почему именно сейчас.
BVernon 26.01.2018 22:42:56
... или вы можете просто использовать выражение фильтра в методе Select: var results = myDataTable.Select ("RowNo = 1"); Это возвращает массив DataRow.
Ishikawa 16.12.2019 11:48:37

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

49
14.08.2008 11:07:52
Если вы попробуете это, вы увидите , что не будет работать , если вы не поставить типа конкретного на myRowили использовании Cast<DataRow>()на Rows. Лучше использовать AsEnumerable().
NetMage 15.01.2020 23:48:45
@NetMage это сработало 12 лет назад, когда я его опубликовал. Пока у вас есть, System.Linqа System.Data.DataSetExtensionsзатем myDataTable.Rowsвозвращает перечислимую коллекцию в DataRowлюбом случае. Это могло измениться, прошло уже десять лет с тех пор, как я его использовал.
Keith 16.01.2020 07:24:58
Интересно - я думаю, что он был изменен в какой-то момент, так как он не работает на .Net или .Net Core сейчас.
NetMage 16.01.2020 19:06:32
@NetMage да, я не удивлен, что DataSetрасширения не попали в .NET Core или .NET Standard, они уже устарели, когда я опубликовал этот ответ. Я действительно не буду использовать DataSetв новых проектах, есть гораздо лучшие модели доступа к данным, как для простоты кодирования и производительности.
Keith 16.01.2020 23:16:42
Они есть, но DataRowCollectionне реализуются IEnumerable<T>просто IEnumerableи поэтому не работают со строго типизированным LINQ.
NetMage 16.01.2020 23:19:00
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow
129
5.03.2009 02:53:10
Как насчет выбора нескольких строк, а не только строки 1?
Adjit 29.04.2016 15:35:26
Просто удалите строку «где», и вы получите все строки
JoelFan 1.05.2016 02:48:59
Да, это то, как я использую это, за исключением замены (int)myRow["RowNo"]на общую форму myRow.Field<int>("RowNo")для более удобной поддержки типов, допускающих обнуление.
Jonas 25.08.2017 14:35:46
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")                         
                    };

поля name и age теперь являются частью объекта запроса и доступны следующим образом: Console.WriteLine (query.name);

38
23.11.2019 13:08:01
Как я использую имя? Например, MessageBox.Show(name)не определено.
user1372430 30.09.2014 22:54:44

Использование 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();
29
11.02.2014 04:10:38
AsDataView не отображается в Intellisense для меня. Я включил использование System.Data.Linq и использование System.Linq, но все равно он не работает. Вы знаете, что мне не хватает? Заранее спасибо.
Naomi 9.05.2013 19:27:30
@ Наоми System.Data.DataSetExtensions.
Louis Waweru 19.02.2014 16:51:07
//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();
 // 
29
11.12.2019 08:43:22

Попробуй это

var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ; 
11
18.05.2012 07:51:09

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

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
7
11.02.2014 04:07:40

Скорее всего, классы для 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);
11
15.12.2017 12:16:07

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

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

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

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

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

5
6.11.2013 09:44:54
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
9
1.02.2014 12:16:51
Этот ответ так же много вопросов с ним.
Mr Anderson 15.08.2016 18:05:19

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

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 );
}
6
20.04.2017 14:17:10

В моем приложении я обнаружил, что использование LINQ to Datasets с расширением AsEnumerable () для DataTable, как было предложено в ответе, было чрезвычайно медленным. Если вы заинтересованы в оптимизации скорости, используйте библиотеку Джеймса Ньютонкинга 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);
8
14.10.2014 17:51:10
Я сомневаюсь, что это быстрее, в общих случаях. Это накладные расходы на две сериализации, одну десериализацию и одну операцию синтаксического анализа. Несмотря на это, я понизил голосование, потому что это не является кратким, то есть сериализация / десериализация не ясно дает понять, что цель состоит в том, чтобы фильтровать список.
an phu 7.08.2015 22:09:42
@an phu, с помощью метода расширения .AsEnumerable создает коллекцию тяжеловесных System.Data.DataRowобъектов. Таблица сериализованных и проанализированных данных создает облегченные данные, состоящие только из имен столбцов и значений каждой строки. Когда запрос выполняется, он загружает данные в память, что для большого набора данных может включать обмен. Иногда накладные расходы на несколько операций меньше, чем накладные расходы на копирование больших объемов данных в память и из нее.
LandedGently 10.08.2015 19:42:22

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

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

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

if(results != null) 
    var foo = results["ColName"].ToString()
12
18.03.2015 22:13:55
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;
7
4.08.2015 07:32:55

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

Мне нравится использовать этот .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 ( ссылка )

35
5.09.2019 15:32:31
Это работает без ссылки на System.Data.DataSetExtensions.
user423430 19.12.2017 15:09:13

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

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
22
15.12.2017 12:15:34
Я предпочитаю « Объединение методов » (как вы сделали здесь), а не « Синтаксис запроса » (в принятом ответе) просто потому, что это базовое предложение where, которое помещается в одну строку и все еще очень читабельно. Каждому свое.
MikeTeeVee 10.08.2016 07:44:29

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

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();
7
25.10.2017 16:04:52

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

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"),    
});
5
25.09.2018 14:33:48
Мир сошел с ума? Что не так с sql? DataRow [] drs = dt.Select ("id = 1"); Может быть, это слишком просто.
Programnik 23.09.2018 23:17:42
                    //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});
0
22.12.2019 13:52:01

Я предлагаю следующее решение:

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

Глядя на документацию DataView , первое, что мы можем увидеть, это:

Представляет настраиваемое представление DataTable с возможностью привязки к данным для сортировки, фильтрации, поиска, редактирования и навигации.

Из этого я получаю то, что DataTable предназначен только для хранения данных, а DataView позволяет нам «запрашивать» данные из DataTable.

Вот как это работает в данном конкретном случае:

Вы пытаетесь реализовать оператор SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

в «DataTable язык». В C # мы читаем это так:

FROM myDataTable
WHERE RowNo = 1
SELECT *

который выглядит в C # так:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *
0
19.08.2019 14:18:58