LINQ-запрос к DataTable

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

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

Это не разрешено. Как мне заставить что-то подобное работать?

Я поражен тем, что запросы LINQ не разрешены в таблицах данных!

Вы можете найти больше примеров LINQ / Lambda из webmingle.blogspot.com/2010_09_01_archive.html

user562221 17.02.2011 22:18

Это потому, что таблицы данных предшествуют LINQ на некоторое количество лет. Вместо этого используйте строго типизированную таблицу данных; лучший опыт во всех отношениях, чем этот строго типизированный, побеждающий intellisense мусор dt.Rows["FirstName] .. Со строго типизированной таблицей (добавьте файл типа DataSet в свой проект и создайте таблицы внутри него в визуальном дизайнере) вы просто пишете, например, myStronglyTpedDataset.Person.Where(p => p.FirstName == "John") - все волшебство, чтобы это произошло, уже сделано

Caius Jard 25.11.2020 14:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 064
2
976 436
23
Перейти к ответу Данный вопрос помечен как решенный

Ответы 23

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

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

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

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

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

Поскольку DataTable.Rows не реализует IEnumerable, я не могу понять, как этот запрос может быть скомпилирован.

onedaywhen 11.07.2016 11:45

@oneday, когда я только что увидел, как это делается в каком-то коде, и он компилируется. Пытаюсь понять почему прямо сейчас.

BVernon 27.01.2018 01:42

... или вы можете просто использовать выражение фильтра в методе Select: var results = myDataTable.Select ("RowNo = 1"); Это возвращает массив DataRow.

Ishikawa 16.12.2019 14:48

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

Если вы попробуете это сделать, вы обнаружите, что это не сработает, если вы не установите определенный тип на myRow или не используете Cast<DataRow>() на Rows. Лучше использовать AsEnumerable().

NetMage 16.01.2020 02:48

@NetMage это сработало 12 лет назад, когда я опубликовал его. Пока у вас есть System.Linq и System.Data.DataSetExtensions, myDataTable.Rows в любом случае возвращает перечисляемую коллекцию DataRow. Это могло измениться, прошло десять лет с тех пор, как я им пользовался.

Keith 16.01.2020 10:24

Интересно - я предполагаю, что в какой-то момент он был изменен, так как сейчас он не работает в .Net или .Net Core.

NetMage 16.01.2020 22:06

@NetMage да, я не удивлен, что расширения DataSet не вошли в .NET Core или .NET Standard, они уже устарели, когда я опубликовал этот ответ. Я бы действительно не стал использовать DataSet в новых проектах, есть гораздо лучшие модели доступа к данным, как по простоте кодирования, так и по производительности.

Keith 17.01.2020 02:16

Они есть, но DataRowCollection не реализует IEnumerable<T>, а только IEnumerable, и поэтому не работает со строго типизированным LINQ.

NetMage 17.01.2020 02:19
Ответ принят как подходящий

Вы не можете запрашивать коллекцию РядовDataTable, поскольку 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> в DataTable, используйте расширение CopyToDataTable().

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

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

Версия VB: Dim results = From myRow In myDataTable.AsEnumerable _ Где myRow.Field ("RowNo") = 1 _ Выбрать myRow

Jeff 30.07.2009 00:46

У меня уже была ссылка на упомянутую dll, но не было using System.Data;

Luke Duddridge 31.05.2011 14:37

Как мне вернуть DataTable из результатов var?

Cannon 11.08.2011 21:50

Версия VB должна вставить (Of String) между myRow.Field и ("RowNo"). Эта часть должна читать: myRow.Field (Of String) ("RowNo") = 1 - Ссылка на комментарий @Cros.

yougotiger 19.06.2012 02:26

это решение излишне сложно. Вместо этого используйте myDataTable.Rows, как предложил @JoelFan.

The Conspiracy 25.06.2014 22:46

@Markus Чтобы прояснить, причина того, что решение @JoelFan работает с myDataTable.Rows, заключается в том, что переменная myRow явно приводится к DataRow. Когда он компилируется, этот запрос переписывается на myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Лично я не считаю вызов AsEnumerable() более сложным, чем вызов Cast<DataRow>(). Насколько я знаю, производительность такая же, так что это вопрос предпочтений.

Collin K 26.06.2014 20:29

...If you need to convert IEnumerable<DataRow> to aDataTable, use the CopyToDataTable() extension. это я искал;)

bonCodigo 12.07.2014 04:15

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

Как насчет выбора нескольких строк вместо одной строки?

Adjit 29.04.2016 18:35

Просто удалите строку "где", и вы получите все строки

JoelFan 01.05.2016 05:48

Да, именно так я и делаю это, за исключением замены (int)myRow["RowNo"] на общую форму myRow.Field<int>("RowNo") для более удобной поддержки типов, допускающих значение NULL.

Jonas 25.08.2017 17:35

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

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

Как я использую имя? Например, MessageBox.Show(name) не определен.

user1372430 01.10.2014 02:54

Использование 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();

AsDataView не отображается в Intellisense для меня. Я включил использование System.Data.Linq и использование System.Linq, но все равно он не работает. Вы знаете, что мне не хватает? Заранее спасибо.

Naomi 09.05.2013 23:27

@Naomi Это идет от System.Data.DataSetExtensions.

Louis Waweru 19.02.2014 20:51

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

Попробуй это

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

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

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

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

Бывший. Имя класса 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);

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

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

Это ответ, так как много вопросов с ним.

Mr Anderson 15.08.2016 21:05

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

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

В своем приложении я обнаружил, что использование 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);

Я сомневаюсь, что в общем случае это быстрее. Он включает в себя две операции сериализации, одну десериализацию и одну операцию синтаксического анализа. Тем не менее, я проголосовал против, потому что он не является кратким, то есть сериализация / десериализация не дает понять, что цель состоит в том, чтобы отфильтровать список.

an phu 08.08.2015 01:09

@an phu с использованием метода расширения .AsEnumerable создает коллекцию тяжелых объектов System.Data.DataRow. Таблица сериализованных и проанализированных данных создает облегченные данные, состоящие только из имен столбцов и значений каждой строки. Когда запрос выполняется, он загружает данные в память, что для большого набора данных может включать подкачку. Иногда накладные расходы на несколько операций меньше, чем накладные расходы на копирование больших объемов данных в память и из нее.

LandedGently 10.08.2015 22:42

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

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

Затем, если вам нужно конкретное значение:

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

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

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

Мне нравится использовать метод .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);

Как отмечено в комментариях, не требует System.Data.DataSetExtensions или каких-либо других сборок (Ссылка)

Это работает без ссылки на System.Data.DataSetExtensions.

user423430 19.12.2017 18:09

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

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

Я предпочитаю «Цепочка методов» (как вы это сделали здесь), а не «Синтаксис запроса» (в принятом ответе) просто потому, что это базовое предложение where, которое умещается в одной строке и по-прежнему хорошо читается. Каждому свое.

MikeTeeVee 10.08.2016 10:44

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

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

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

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

Мир сошел с ума? Что не так с sql? DataRow [] drs = dt.Select ("id = 1"); Может, это слишком просто.

Programnik 24.09.2018 02:17

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

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

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

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

Represents a databindable, customized view of a DataTable for sorting, filtering, searching, editing, and navigation.

Из этого я понимаю, что 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 *

Другие вопросы по теме