dataTable1
とdataTable2
に2つの単純なSQLクエリを入力する次のコードがあります。dataTableSqlJoined
は同じテーブルから入力されますが、結合されます。
SQLを使用して作成されたかのようにdataTableLinqJoined
を作成できるLINQクエリを記述しようとしています。以下の私の例では、dataTable1からの値のみを返します。
私が抱えている問題は、linqクエリのSELECT
に何を入れるかです。両方のDataRowからすべての列を含む新しいDataRowを作成するにはどうすればよいですか。実行時まで、クエリの正確な列名/スキーマはわかりません。
sqlCommand = new SqlCommand("SELECT ID, A, B FROM Table1", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTable1 = new DataTable();
sqlAdapter.Fill(dataTable1);
sqlCommand = new SqlCommand("SELECT ID, C, D FROM Table2", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTable2 = new DataTable();
sqlAdapter.Fill(dataTable2);
sqlCommand = new SqlCommand("SELECT Table1.ID, A, B, Table2.ID, C, D FROM Table1 INNER JOIN Table2 ON Table1.ID = Table2.ID", sqlConnection, sqlTransaction);
sqlAdapter = new SqlDataAdapter(sqlCommand);
DataTable dataTableSqlJoined = new DataTable();
sqlAdapter.Fill(dataTableSqlJoined);
var dataRows =
from
dataRows1 in dataTable1.AsEnumerable()
join
dataRows2 in dataTable2.AsEnumerable()
on
dataRows1.Field<int>("ID") equals dataRows2.Field<int>("ID")
select
dataRows1; // + dataRows2;
DataTable dataTableLinqJoined = dataRows.CopyToDataTable();
もう少し背景として、結合されたクエリは非常にDB集約的であり、パフォーマンスの問題を引き起こしています。最初のクエリによって返されるデータはかなり静的であり、大量にキャッシュされる可能性があります。 2番目のクエリによって返されるデータは常に変化しますが、実行が高速であるため、キャッシュする必要はありません。また、結合されたDataTableの受け渡しに依存するコードがたくさんあるため、異なる形式でデータを渡すために利用できる実行可能なオプションは多くありません。
このページはもう見ましたか?
方法:Visual C#.NETでDataSet JOINヘルパークラスを実装する
そのアプローチではLINQyが十分でない場合は、行データをオブジェクト配列に分割できます。
_DataTable targetTable = dataTable1.Clone();
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc =>
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
targetTable.Columns.AddRange(dt2Columns.ToArray());
var rowData =
from row1 in dataTable1.AsEnumerable()
join row2 in dataTable2.AsEnumerable()
on row1.Field<int>("ID") equals row2.Field<int>("ID")
select row1.ItemArray.Concat(row2.ItemArray).ToArray();
foreach (object[] values in rowData)
targetTable.Rows.Add(values);
_
それはあなたがそれを作ることができるようになるだろうと同じくらい簡潔だと思います、そして私は理由を説明します:それはスキーマです。
DataRow
は独立したオブジェクトではありません。それはそれを所有するDataTable
に依存し、それなしでは生きられません。 「切断された」DataRow
を作成するためのサポートされている方法はありませんCopyToDataTable()
拡張メソッドは、1つのDataTable
に既に存在する行に対して機能し、ソースからスキーマをコピーするだけです(すべてのDataRow
は、行自体をコピーする前に、親Table
への参照を持っていることに注意してください)(おそらくImportRow
を使用します) 、実際にはReflectorを開いて確認していません)。
この場合、作成する必要のある新しいスキーマがあります。 (新しい)行を作成する前に、それらを保持するテーブルを作成する必要があります first 、つまり、上記のメソッドの上部に少なくとも3行のコードを記述します。
その後、最終的に行を作成できます。ただし、DataTable
とそれに関連付けられているDataRowCollection
は、一度に複数の行を追加するメソッドを公開していないため、一度に1つしか作成できません。もちろん、DataRowCollection
に独自の拡張メソッドを追加して、この「見栄え」を良くすることができます。
_public static void AddRange(this DataRowCollection rc,
IEnumerable<object[]> tuples)
{
foreach (object[] data in tuples)
rc.Add(tuples);
}
_
次に、最初のメソッドでforeach
を削除して、次のものに置き換えることができます。
_targetTable.Rows.AddRange(rowData);
_
それは実際には冗長性を移動するだけであり、それを排除するものではありません。
結論としては、レガシーのDataSet
クラス階層を使用している限り、常に少しばかり問題が発生します。 LinqからDataSetへの拡張は素晴らしいですが、これらは拡張にすぎず、上記の制限を変更することはできません。
それは素晴らしかったです。しかし、LINQyコードにいくつかの拡張機能を追加したいと思います。 dataTable2からターゲットテーブルに列を追加する際、(結合先の)ターゲットテーブルに既にいくつかの列が存在している可能性があります。さあ、行きます。
DataTable targetTable = dataTable1.Clone();
var dt2Columns = dataTable2.Columns.OfType<DataColumn>().Select(dc =>
new DataColumn(dc.ColumnName, dc.DataType, dc.Expression, dc.ColumnMapping));
var dt2FinalColumns=from dc in dt2Columns.AsEnumerable()
where targetTable.Columns.Contains(dc.ColumnName) == false
select dc;
targetTable.Columns.AddRange(dt2FinalColumns.ToArray());
var rowData =from row1 in dataTable1.AsEnumerable()
join row2 in dataTable2.AsEnumerable()
on row1.Field<int>("ID") equals row2.Field<int>("ID")
select row1.ItemArray.Concat(row2.ItemArray.Where(r2=> row1.ItemArray.Contains(r2)==false)).ToArray();
foreach (object[] values in rowData)
targetTable.Rows.Add(values);
これが私のような人たちに役立つことを願っています。
バカみたいに聞こえたらごめんなさい。
私は、ファイナルテーブルを準備する必要があると思います(テーブルAとテーブルBのすべてのフィールドを含む)。
そして、LINQを使用する代わりに、結合を実行してから、結果に対してForEach
を実行し、最終的なデータテーブルに値を挿入します。
疑似コード:
dt1.Join(dt2).Where(...)。ForEach(row =>匿名オブジェクトのコンテンツを読み取り、finalTable.Rowsに追加するコード)
select new {
ID = dataRows1.ID, // no need to select dataRows2.ID, because of JOIN.
A = dataRows1.A,
B = dataRows1.B,
C = dataRows2.C,
D = dataRows2.D
};