从与LINQ连接的两个DataTable创建组合的DataTable。 C#

时间:2010-03-04 13:56:04

标签: c# linq join datatable

我有以下代码使用两个简单的SQL查询填充dataTable1dataTable2dataTableSqlJoined从相同的表填充但是连接在一起。

我正在尝试编写一个可以创建dataTableLinqJoined的LINQ查询,就好像它是使用SQL创建的一样。在下面的示例中,它仅返回dataTable1中的值。

我遇到的问题是放在linq查询的SELECT中。如何创建包含DataRows中所有列的新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();

对于更多背景知识,合并后的查询是数据库密集型的,并且会导致性能问题。第一个查询返回的数据是相当静态的,可以大量缓存。第二个查询返回的数据不断变化但运行速度很快,因此不需要缓存。还有很多代码依赖于组合DataTable的传递,因此在以不同格式传递数据时没有太多可行的选项。

4 个答案:

答案 0 :(得分:20)

你看过这个页了吗?

HOW TO: Implement a DataSet JOIN helper class in Visual C# .NET

如果这种方法对你来说不够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()扩展方法适用于一个DataTable中已存在的行,只需从源中复制架构(请记住,每个DataRow都有对其父Table的引用)在复制行本身之前(最有可能使用ImportRow,虽然我实际上没有打开Reflector来检查)。

在这种情况下,您需要创建一个新架构。在创建任何(新)行之前,您需要创建表来保存它们 first ,这意味着在上面的方法顶部至少编写3行代码。

然后您最终可以创建行 - 但一次只能创建一行,因为DataTable及其关联的DataRowCollection不会公开任何方法来一次添加多行。当然,您可以为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 to DataSet扩展很好,但它们只是扩展,不能改变上面的限制。

答案 1 :(得分:5)

Aaronaught很棒。但是想为LINQy代码添加一些增强功能。在将dataTable2中的列添加到Target表时,Target表中可能已存在少量列(我们正在加入)。所以我们走了。

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

希望这会对像我这样的人有所帮助。

答案 2 :(得分:1)

如果我听起来像个白痴,请原谅我。

我认为,你应该准备好决赛桌(包括表A和表B的所有字段) 并且,不是使用LINQ,而是使用连接&amp;然后在结果&amp;上做ForEach将值插入最终数据表。

伪代码

dt1.Join(dt2).Where(...)。ForEach(row =&gt;代码读取匿名对象的内容并将其添加到finalTable.Rows)

答案 3 :(得分:0)

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