Permintaan LINQ pada DataTable

1031

Saya mencoba untuk melakukan query LINQ pada objek DataTable dan anehnya saya menemukan bahwa melakukan query seperti pada DataTable tidak mudah. Sebagai contoh:

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

Ini tidak diizinkan Bagaimana saya membuat sesuatu seperti ini berfungsi?

Saya kagum bahwa kueri LINQ tidak diizinkan di DataTables!

Calanus
sumber
3
Anda dapat menemukan lebih banyak contoh LINQ / Lambda dari webmingle.blogspot.com/2010_09_01_archive.html

Jawaban:

1279

Anda tidak bisa query terhadap DataTable's Rows koleksi, karena DataRowCollectiontidak melaksanakan IEnumerable<T>. Anda perlu menggunakan AsEnumerable()ekstensi untuk DataTable. Seperti itu:

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

Dan seperti yang dikatakan @Keith , Anda perlu menambahkan referensi ke System.Data.DataSetExtensions

AsEnumerable()kembali IEnumerable<DataRow>. Jika Anda perlu mengonversi IEnumerable<DataRow>ke DataTable, gunakan CopyToDataTable()ekstensi.

Di bawah ini adalah kueri dengan Ekspresi Lambda,

var result = myDataTable
    .AsEnumerable()
    .Where(myRow => myRow.Field<int>("RowNo") == 1);
Collin K
sumber
8
Versi VB: Hasil redup = Dari myRow Di myDataTable.AsEnumerable _ Where myRow.Field ("RowNo") = 1 _ Pilih myRow
Jeff
15
Saya sudah memiliki referensi ke dll yang disebutkan, tetapi hilang using System.Data;
Luke Duddridge
5
Versi VB perlu memasukkan (Of String) antara myRow.Field dan ("RowNo"). Bagian itu seharusnya berbunyi: myRow.Field (Of String) ("RowNo") = 1 - Referensi @Cros komentar.
yougotiger
8
solusi ini tidak perlu rumit. Gunakan myDataTable.Rowssebaliknya seperti yang disarankan @JoelFan.
Konspirasi
10
@ Markus Hanya untuk memperjelas, alasan bahwa solusi @ JoelFan berfungsi myDataTable.Rowsadalah karena myRowvariabel secara eksplisit dilemparkan ke DataRow. Ketika dikompilasi, kueri itu ditulis ulang myDataTable.Rows.Cast<DataRow>().Where(myRow => (int)myRow["RowNo"] == 1). Secara pribadi, saya tidak menemukan panggilan ke yang AsEnumerable()lebih rumit daripada panggilan ke Cast<DataRow>(). Sejauh yang saya tahu, kinerjanya sama, jadi itu hanya masalah preferensi.
Collin K
129
var results = from DataRow myRow in myDataTable.Rows
    where (int)myRow["RowNo"] == 1
    select myRow
JoelFan
sumber
2
Bagaimana dengan memilih beberapa baris, bukan hanya baris 1?
Adjit
2
Hapus saja baris "di mana" dan Anda akan mendapatkan semua baris
JoelFan
1
Ya, inilah yang saya gunakan untuk melakukannya, kecuali untuk mengganti (int)myRow["RowNo"]dengan bentuk umum myRow.Field<int>("RowNo")untuk lebih mudah mendukung jenis nullable.
Jonas
69

Bukannya mereka sengaja tidak diizinkan di DataTables, hanya saja DataTable pra-tanggal konstruksi IQneryable dan IEnumerable generik di mana pertanyaan Linq dapat dilakukan.

Kedua antarmuka membutuhkan semacam validasi keamanan jenis. DataTable tidak diketik dengan kuat. Ini adalah alasan yang sama mengapa orang tidak dapat melakukan query terhadap ArrayList, misalnya.

Agar Linq berfungsi, Anda perlu memetakan hasil Anda terhadap objek yang aman jenis dan sebagai gantinya kueri terhadap itu.

Jon Limjap
sumber
49

Seperti yang dikatakan @ 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

Anda juga perlu menambahkan referensi proyek System.Data.DataSetExtensions

Keith
sumber
1
Jika Anda mencoba ini, Anda akan menemukan itu tidak akan bekerja kecuali Anda menempatkan jenis tertentu pada myRowatau penggunaan Cast<DataRow>()di Rows. Lebih baik digunakan AsEnumerable().
NetMage
1
@ NetMage ini berfungsi 12 tahun yang lalu ketika saya mempostingnya. Selama Anda memiliki System.Linqdan System.Data.DataSetExtensionskemudian myDataTable.Rowsmengembalikan koleksi enumerable DataRowtoh. Itu mungkin telah berubah, sudah satu dekade sejak saya menggunakannya.
Keith
1
Menarik - Saya kira itu berubah di beberapa titik, karena tidak berfungsi di .Net atau .Net Core sekarang.
NetMage
1
@ NetMage ya, saya tidak terkejut DataSetekstensi tidak membuatnya menjadi .NET Core atau .NET Standard, mereka sudah usang ketika saya memposting jawaban ini. Saya benar-benar tidak akan menggunakan DataSetproyek baru, ada model akses data yang jauh lebih baik, baik untuk kemudahan pengkodean dan kinerja.
Keith
1
Mereka ada di sana, tetapi DataRowCollectiontidak menerapkan IEnumerable<T>adil IEnumerabledan tidak bekerja dengan LINQ yang sangat diketik.
NetMage
39
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")                         
                    };

bidang nama dan usia sekarang menjadi bagian dari objek kueri dan dapat diakses seperti: Console.WriteLine (query.name);

Ravi
sumber
Bagaimana saya menggunakan nama? Misalnya, MessageBox.Show(name)tidak ditentukan.
35

Saya menyadari ini telah dijawab beberapa kali, tetapi hanya untuk menawarkan pendekatan lain:

Saya suka menggunakan .Cast<T>()metode ini, ini membantu saya menjaga kewarasan dalam melihat tipe eksplisit yang didefinisikan dan jauh di dalam hati saya pikir .AsEnumerable()menyebutnya:

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

atau

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

Seperti disebutkan dalam komentar, tidak ada majelis lain yang diperlukan karena merupakan bagian dari Linq ( Referensi )

vandsh
sumber
5
Ini berfungsi tanpa merujuk System.Data.DataSetExtensions.
user423430
29

Menggunakan LINQ untuk memanipulasi data dalam 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();
Salim
sumber
1
AsDataView tidak muncul di Intellisense untuk saya. Saya termasuk menggunakan System.Data.Linq dan menggunakan System.Linq tetapi masih tidak berfungsi. Apakah Anda tahu apa yang saya lewatkan? Terima kasih sebelumnya.
Naomi
@Nomi berasal dari System.Data.DataSetExtensions.
Louis Waweru
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();
 // 
sushil pandey
sumber
22

Coba baris kueri sederhana ini:

var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
Mohit Verma
sumber
4
Saya lebih suka " Metode Chaining " (seperti yang Anda lakukan di sini) daripada " Sintaks Kueri " (dalam jawaban yang diterima) hanya karena ini adalah klausa dasar mana yang cocok pada satu baris dan masih sangat mudah dibaca. Untuk masing-masing.
MikeTeeVee
16

Anda dapat menggunakan LINQ untuk objek pada koleksi Baris, seperti:

var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
David Wengier
sumber
1
Karena DataTable.Rowstidak diterapkan IEnumerable, saya tidak dapat melihat bagaimana kueri ini dapat dikompilasi.
onedaywhen
@onedaywhen Saya baru saja melihat ini dilakukan dalam beberapa kode dan itu mengkompilasi. Mencoba mencari tahu mengapa sekarang.
BVernon
... atau Anda bisa menggunakan ekspresi filter dalam metode Select: var results = myDataTable.Select ("RowNo = 1"); Ini mengembalikan array DataRow.
Ishikawa
12

Ini adalah cara sederhana yang berfungsi untuk saya dan menggunakan ekspresi lambda:

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

Kemudian jika Anda menginginkan nilai tertentu:

if(results != null) 
    var foo = results["ColName"].ToString()
Matt Kemp
sumber
11

Coba ini

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

Kemungkinan besar, kelas untuk DataSet, DataTable dan DataRow sudah didefinisikan dalam solusi. Jika demikian, Anda tidak perlu referensi DataSetExtensions.

Ex. Nama kelas DataSet-> CustomSet, Nama kelas DataRow-> CustomTableRow (dengan kolom yang ditentukan: RowNo, ...)

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

Atau (seperti yang saya inginkan)

var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);
xadriel
sumber
9
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
Vinay
sumber
Jawaban ini karena banyak masalah dengannya.
Mr Anderson
8

Dalam aplikasi saya, saya menemukan bahwa menggunakan LINQ untuk Kumpulan Data dengan ekstensi AsEnumerable () untuk DataTable seperti yang disarankan dalam jawabannya sangat lambat. Jika Anda tertarik untuk mengoptimalkan kecepatan, gunakan perpustakaan Json.Net James Newtonking ( 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);
Mendarat dengan lembut
sumber
Saya ragu ini lebih cepat, dalam kasus umum. Ini memiliki overhead dua serialisasi, satu deserialization dan satu operasi parsing. Bagaimanapun, saya downvoted karena tidak ringkas, yaitu serialisasi / deserialisasi tidak menjelaskan bahwa tujuannya adalah untuk menyaring daftar.
an phu
@ an phu, menggunakan metode ekstensi .AsEnumerable membuat koleksi System.Data.DataRowobjek kelas berat . Tabel data berseri dan parsing membuat data ringan yang hanya terdiri dari nama kolom dan nilai setiap baris. Ketika kueri berjalan, itu akan memuat data ke dalam memori, yang untuk dataset besar mungkin melibatkan swapping. Terkadang, overhead beberapa operasi kurang dari overhead menyalin sejumlah besar data masuk dan keluar dari memori.
LandedGently
7

Untuk VB.NET Kode akan terlihat seperti ini:

Dim results = From myRow In myDataTable  
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
Abdul Saboor
sumber
7
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
                             select myRow["server"].ToString() ;
Iman
sumber
7

Contoh tentang cara mencapai ini disediakan di bawah ini:

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();
Ryan Gavin
sumber
6

Coba ini...

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 );
}
Uthaiah
sumber
5

Anda bisa membuatnya bekerja dengan elegan melalui linq seperti ini:

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

Atau seperti linq dinamis ini (AsDynamic dipanggil langsung di DataSet):

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

Saya lebih suka pendekatan terakhir sedangkan yang paling fleksibel. PS: Jangan lupa menghubungkan System.Data.DataSetExtensions.dllreferensi

AuthorProxy
sumber
5

Anda dapat mencoba ini, tetapi Anda harus yakin jenis nilai untuk setiap Kolom

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"),    
});
Gabriel Martinez Bustos
sumber
Apakah dunia sudah gila? Apa yang salah dengan sql? DataRow [] drs = dt.Select ("id = 1"); Mungkin ini terlalu mudah.
Programnik
0

Saya mengusulkan solusi berikut:

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

Melihat Dokumentasi DataView , hal pertama yang dapat kita lihat adalah ini:

Merupakan tampilan DataTable yang dapat diubah menurut data, untuk menyortir, memfilter, mencari, mengedit, dan navigasi.

Apa yang saya dapatkan dari ini adalah bahwa DataTable hanya dimaksudkan untuk menyimpan data dan DataView ada memungkinkan kita untuk "query" terhadap DataTable.

Inilah cara kerjanya dalam kasus khusus ini:

Anda mencoba menerapkan Pernyataan SQL

SELECT *
FROM myDataTable
WHERE RowNo = 1

dalam "Bahasa DataTable". Dalam C # kita akan membacanya seperti ini:

FROM myDataTable
WHERE RowNo = 1
SELECT *

yang terlihat dalam C # seperti ini:

DataView view = new DataView(myDataTable);  //FROM myDataTable
view.RowFilter = "RowNo = 1";  //WHERE RowNo = 1
DataTable results = view.ToTable(true);  //SELECT *
Alan
sumber
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});
chandra rv
sumber