代码之家  ›  专栏  ›  技术社区  ›  Robin Day

从与Linq联接的两个数据表创建组合的数据表。C.*

  •  22
  • Robin Day  · 技术社区  · 16 年前

    我有以下代码可以填充 dataTable1 dataTable2 通过两个简单的SQL查询, dataTableSqlJoined 由同一个表填充,但连接在一起。

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

    我的问题是在 SELECT Linq查询的。如何创建包含两个数据行中所有列的新数据行。直到运行时,我才知道查询的确切列名称/架构。

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

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

    4 回复  |  直到 14 年前
        1
  •  20
  •   Robin Day    16 年前

    你看过这页了吗?

    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 没有它就不能生存。有 没有支持的方式 创建“断开连接” 数据行 ; CopyToDataTable() 扩展方法适用于已存在于其中的行 数据表 只需从源代码复制模式(记住,每个 数据行 具有对其父级的引用 Table )在复制行之前(很可能使用 ImportRow 尽管我还没有打开反射镜进行检查)。

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

    然后您可以最终创建行,但一次只能创建一行,因为 数据表 以及它的关联 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扩展很好,但是它们只是扩展,不能改变上面的限制。

        2
  •  5
  •   suryakiran    14 年前

    奥罗诺,太好了。但希望在Linqy代码中添加一些增强功能。将数据表2中的列添加到目标表时,目标表(我们正在加入的表)中可能已经没有列了。我们开始吧。

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

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

        3
  •  1
  •   shahkalpesh    16 年前

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

    我认为,您应该准备好最后一张表(表A和表B的所有字段)。
    并且,不要使用LINQ,而是执行联接,然后执行 ForEach 在结果中,将值插入最终数据表。

    伪代码 :

    dt1.join(dt2).where(…).foreach(row=>用于读取匿名对象内容的代码,并将其添加到finalTable.rows)

        4
  •  0
  •   Roger Lipscombe    16 年前
    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 
    };