Запит LINQ на DataTable


1031

Я намагаюся виконати запит LINQ на об’єкті DataTable, і дивно, я виявляю, що виконання таких запитів у DataTables не є простим. Наприклад:

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

Це заборонено. Як я можу щось подібне працювати?

Я вражений тим, що запити LINQ не дозволяються на DataTables!


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

Відповіді:


1279

Ви не можете запитувати проти колекції рядківDataTable 's , оскільки не реалізується . Для цього потрібно використовувати розширення . Так:DataRowCollectionIEnumerable<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()розширення.

Нижче наводиться запит із Lambda Expression,

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

8
Версія VB: Результати затемнення = Від myRow У myDataTable.AsEumerable _ Where myRow.Field ("RowNo") = 1 _ Виберіть myRow
Jeff

15
Я вже мав посилання на згадуваний dll, але його бракувалоusing System.Data;
Люк Даддрідж

5
Версія VB повинна вставляти (String) між myRow.Field та ("RowNo"). Ця частина повинна читати: myRow.Field (Of String) ("RowNo") = 1 - Посилання @Cros коментар.
yougotiger

8
це рішення непотрібно складне. Використовуйте myDataTable.Rowsзамість того, як запропонував @JoelFan.
Змова

10
@Markus Просто для уточнення, причина, з якою працює рішення @ JoelFan, myDataTable.Rowsполягає в тому, що myRowзмінна явно передана DataRow. Коли він складається, цей запит переписується в myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Особисто я не вважаю заклик нічим AsEnumerable()складнішим, ніж дзвінок Cast<DataRow>(). Наскільки я знаю, продуктивність однакова, тому це просто питання уподобань.
Collin K

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

2
Як щодо вибору кількох рядків, а не просто рядка 1?
Аджит

2
Просто видаліть рядок "де", і ви отримаєте всі рядки
JoelFan

1
Так, я це роблю для цього, за винятком заміни (int)myRow["RowNo"]на загальну форму myRow.Field<int>("RowNo")на більш зручну підтримку змінних типів.
Йонас

69

Справа не в тому, що їх навмисно заборонено на DataTables, це просто те, що DataTables попередньо датує IQueryable і загальні IEnumerable конструкції, над якими можна виконувати запити Linq.

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

Щоб Linq працював, вам потрібно зіставити результати на об’єкти, що захищають тип, і запитувати проти цього.


49

Як сказав @ 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


1
Якщо ви спробуєте це, ви побачите , що не працюватиме , якщо ви не поставити типу конкретного на myRowабо використання Cast<DataRow>()на Rows. Краще використовувати AsEnumerable().
NetMage

1
@NetMage це працювало 12 років тому, коли я розмістив його. Поки ви маєте, System.Linqа System.Data.DataSetExtensionsпотім myDataTable.Rowsповернете чималу колекцію в DataRowбудь-якому випадку. Це, можливо, змінилося, минуло десятиліття, як я його використав.
Кіт

1
Цікаво - я думаю, він був змінений в якийсь момент, оскільки він не працює .Net або .Net Core зараз.
NetMage

1
@NetMage так, я не здивований, що DataSetрозширення не ввійшли в .NET Core або .NET Standard, вони вже застаріли, коли я опублікував цю відповідь. Я дійсно не використовував би DataSetв нових проектах, є набагато кращі моделі доступу до даних, як для простоти кодування, так і для роботи.
Кіт

1
Вони є, але DataRowCollectionне реалізують IEnumerable<T>просто, IEnumerableтому не працюють із сильно набраним LINQ.
NetMage

38
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 (query.name);


Як я використовую ім'я? Наприклад, MessageBox.Show(name)не визначено.

35

Я усвідомлюю, що на це вже відповіли декілька разів, але просто запропонувати інший підхід:

Мені подобається використовувати .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 ( Довідник )


5
Це працює без посилання на System.Data.DataSetExtensions.
користувач423430

29

Використання 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, але все ще не працює. Ви знаєте, чого я пропускаю? Заздалегідь спасибі.
Наомі

@Naomi Походить від System.Data.DataSetExtensions.
Луї Ваверу

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

Спробуйте цей простий рядок запиту:

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

4
Я вважаю за краще " Метод ланцюжка " (як ви це зробили тут) над " Синтаксисом запитів " (у прийнятій відповіді) просто тому, що це основний пункт, де вміщується в одному рядку і все ще дуже читабельний. Кожному своє.
MikeTeeVee

16

Ви можете використовувати LINQ для об'єктів колекції рядків, наприклад:

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

1
Оскільки DataTable.Rowsне реалізується IEnumerable, я не бачу, як цей запит міг би скластись.
одного дня, коли

@onedaywhen Я щойно бачив, як це робиться в якомусь коді, і він компілюється. Намагаюсь з’ясувати, чому саме зараз.
BVernon

... або ви можете просто використовувати вираз фільтра в методі Select: var results = myDataTable.Select ("RowNo = 1"); Це повертає масив DataRow.
Ішикава

12

Це простий спосіб, який працює для мене і використовує лямбда-вирази:

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

Тоді, якщо ви хочете певного значення:

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


11

Швидше за все, класи для 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);


8

У своїй програмі я виявив, що використання LINQ для наборів даних із розширенням AsEnumerable () для DataTable, як було запропоновано у відповіді, було надзвичайно повільним. Якщо ви зацікавлені в оптимізації швидкості, використовуйте бібліотеку Джеймса Ньютона Джеймса Ньютонкінга ( 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, використовуючи метод .AsEnumerable Extension, створює колекцію важких System.Data.DataRowпредметів. Серіалізована та проаналізована таблиця даних створює полегшені дані, що складаються лише з назв стовпців та значень кожного рядка. Коли запит запускається, він завантажить дані в пам'ять, що для великого набору даних може включати в себе заміну. Іноді накладні витрати на кілька операцій менші, ніж накладні витрати на копіювання великої кількості даних у пам'ять і поза нею.
ПриземливсяГережно

7

Для VB.NET Код буде виглядати приблизно так:

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

Приклад того, як цього досягти, наведено нижче:

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

Спробуйте це...

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

Ви можете зробити його елегантним через 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)

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


5

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

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"); Можливо, це занадто просто.
Програмник

0

Я пропоную наступне рішення:

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

Переглядаючи Документацію DataView , перше, що ми можемо побачити, це таке:

Представляє сумісний з даними персоналізований вигляд таблиці даних для сортування, фільтрації, пошуку, редагування та навігації.

Що я отримую з цього, це те, що DataTable призначений тільки для зберігання даних, а DataView дозволяє нам "запитувати" проти DataTable.

Ось як це працює в даному конкретному випадку:

Ви намагаєтеся реалізувати оператор SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

мовою "Мова даних". У 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
                    //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});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.