DataTableのLINQクエリ


1031

DataTableオブジェクトでLINQクエリを実行しようとしていますが、奇妙なことに、DataTablesでそのようなクエリを実行するのは簡単ではありません。例えば:

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

これは許可されていません。このようなものをどのように機能させるのですか?

LINQクエリがDataTablesで許可されていないことに驚いています!


3
LINQ / Lambdaの例は、webmingle.blogspot.com

回答:


1279

あなたはに対してクエリできない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);

8
VBバージョン:Dim results = From myRow In myDataTable.AsEnumerable _ Where myRow.Field( "RowNo")= 1 _ select myRow
Jeff

15
既に言及したDLLへの参照はありましたが、欠落していましたusing System.Data;
Luke Duddridge、

5
VBバージョンは、myRow.Fieldと( "RowNo")の間に(Of String)を挿入する必要があります。その部分は次のようになります。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 14年

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

2
行1だけでなく、複数の行を選択する場合はどうでしょうか?
Adjit

2
"where"行を削除すると、すべての行が表示されます
JoelFan

1
はい、これは私がそれを行うために使用する方法ですが、null許容型をより便利にサポート(int)myRow["RowNo"]するmyRow.Field<int>("RowNo")ためにジェネリックフォームに置き換える点が異なります。
ジョナス

69

DataTablesで意図的に許可されていなかったわけではなく、Linqクエリを実行できるIQueryableおよび汎用IEnumerable構成よりもDataTablesが先行しているというだけのことです。

どちらのインターフェースでも、ソートタイプセーフの検証が必要です。DataTableは強く型付けされていません。これは、たとえば、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.LinqSystem.Data.DataSetExtensions、その後myDataTable.Rowsの可算集合を返すDataRowとにかくを。それは変わったかもしれませんが、私がそれを使用してから10年になります。
キース

1
興味深い-今は.Netまたは.Net Coreでは機能しないため、いつか変更されたと思います。
NetMage

1
@NetMageはい、DataSet拡張機能が.NET Coreまたは.NET Standardに組み込まれなかったことに驚いていません。この回答を投稿したとき、それらは既に時代遅れでした。私は本当にDataSet新しいプロジェクトでは使用しません。コーディングの容易さとパフォーマンスの両方のために、はるかに優れたデータアクセスモデルがあります。
キース

1
彼らはありますが、DataRowCollection実装していないIEnumerable<T>だけIEnumerableので、強く型付けされたLINQで仕事をしません。
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")                         
                    };

nameおよび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を参照しなくても機能します。
user423430

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

@Naomiから来ていSystem.Data.DataSetExtensionsます。
Louis Waweru 2014

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
(ここで行ったように)「受け入れられた回答」の「クエリ構文」よりも「メソッドチェーン」の方が好きです。これは、これが1行に収まる基本的なwhere句であり、非常に読みやすいためです。それぞれ独自に。
MikeTeeVee

16

次のように、LINQを使用してRowsコレクションのオブジェクトを作成できます。

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

1
のでDataTable.Rows実装されていないIEnumerable、私はこのクエリはコンパイル可能性がどのように見ることができません。
onedaywhen

@oneday私がこれがいくつかのコードで行われているのを見ただけで、コンパイルされます。なぜ今なのか理解しようとしています。
BVernon 2018年

...または、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

これを試して

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

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

私のアプリケーションでは、回答で提案されているように、DataTableのAsEnumerable()拡張を使用してLINQ to Datasetsを使用すると非常に遅いことがわかりました。速度の最適化に興味がある場合は、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);

一般的なケースでは、これはより速いと思います。2つのシリアル化、1つの逆シリアル化、1つの解析操作のオーバーヘッドがあります。いずれにしても、簡潔ではないため、私は反対票を投じました。つまり、シリアル化/逆シリアル化では、リストをフィルター処理することを意図していることが明確にされていません。
2015

@an phu、.AsEnumerable拡張メソッドを使用して、ヘビー級System.Data.DataRowオブジェクトのコレクションを作成します。シリアル化および解析されたデータテーブルは、列名と各行の値のみで構成される軽量データを作成します。クエリが実行されると、データがメモリに読み込まれます。大規模なデータセットの場合、スワップが発生する可能性があります。場合によっては、いくつかの操作のオーバーヘッドが、大量のデータをメモリにコピーしたりメモリからコピーしたりするオーバーヘッドよりも少なくなります。
LandedGently 2015

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"); 多分これは簡単すぎる。
Programnik

0

私は次の解決策を提案します:

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

DataView Documentationを見ると、最初に目につくのはこれです。

並べ替え、フィルタリング、検索、編集、およびナビゲーションのための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
                    //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});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.