【问题标题】:How do I get this Linq query with custom join into a DataSet?如何使用自定义连接将这个 Linq 查询添加到 DataSet 中?
【发布时间】:2013-05-15 20:12:40
【问题描述】:

在使用连接时,我似乎无法超越 Linq 的 select 子句使用的匿名类型。我想将查询结果放入数据表中。这是我在运行时内存中的内容...

DataTable1 (source)
----------
col1A <int> (acting PK, not explicitly defined as such)
col1B <string>
col1C <string>
col1D <string> (FK -> dt2.col2A, also not explicitly defined)
col1E <string>

DataTable2 (source)
----------
col2A <string> (acting PK, also not explicitly defined)
col2B <string>

DataTable3 (destination)
----------
colA <int>
colB <string>
colC <string>
colD <string>

这就是我想要的(SQL 等同于我正在寻找的)...

INSERT INTO DataTable3 (colA, colB, colC, colD)
SELECT  dt1.col1A, dt1.col1B, dt1.col1C, dt2.col2B
FROM DataTable1 dt1
LEFT JOIN DataTable2 dt2 ON (dt1.col1D = dt2.col2A)

向后工作,我看到这条简单的线将枚举查询结果集放入数据表中:

DataTable DataTable3 = query.CopyToDataTable();

这是我尝试过的... 我不知道如何以允许我使用它的方式构建“查询”。 尝试 1(使用 var 查询;这有效,请进一步阅读):

var query =
    from dt1 in DataTable1.AsEnumerable()
    join dt2 in DataTable2.AsEnumerable()
        on dt1.Field<string>("col1D") equals dt2.Field<string>("col2A")
    select new { col1A = dt1.Field<int>("col1A"),
        col1B = dt1.Field<string>("col1B"),
        col1C = dt1.Field<string>("col1C"),
        col2B = dt2.Field<string>("col2B") };

但这在“query.CopyToDataTable()”上给了我这个错误:

"错误 1 ​​类型 'AnonymousType#1' 不能用作类型参数 泛型类型或方法中的“T” 'System.Data.DataTableExtensions.CopyToDataTable(System.Collections.Generic.IEnumerable)'。 没有从“AnonymousType#1”到的隐式引用转换 'System.Data.DataRow'。”

据我推测,此错误是因为“选择新”不会将 DataRows 构建到 .CopyToDataTable() 要求的“查询”中;它只是建立匿名记录。此外,我不知道如何将查询元素转换为 DataRow,因为“由于保护级别,它无法访问”。

尝试 2(使用 IEnumerable 查询;不要费心尝试这个):

IEnumerable<DataRow> query =
    from dt1 in DataTable1.AsEnumerable()
    join dt2 in DataTable2.AsEnumerable()
        on dt1.Field<string>("col1D") equals dt2.Field<string>("col2A")
    select new { col1A = dt1.Field<int>("col1A"),
        col1B = dt1.Field<string>("col1B"),
        col1C = dt1.Field<string>("col1C"),
        col2B = dt2.Field<string>("col2B") };

这会在“加入”时出错:

"错误5 无法隐式转换类型 'System.Collections.Generic.IEnumerable' 到 'System.Collections.Generic.IEnumerable'。一个 存在显式转换(您是否缺少演员表?)”

附带说明,如果我注释掉“join”子句(以及“select”中相应的 dt2 列),我会在“select”上收到此错误:

"错误2 无法隐式转换类型 'System.Data.EnumerableRowCollection' 到 'System.Collections.Generic.IEnumerable'。一个 存在显式转换(您是否缺少演员表?)”

我还尝试使用列预先定义 DataTable3,以给“select”子句提供一个真正的 DataRow,但我仍然无法让“select”利用它。


编辑、详细信息、解决方案: 感谢蒂姆的帮助。我选择了尝试 1(var 查询)。我还预定义了 DataTable3:

DataTable DataTable3 = new DataTable();
DataTable3.Columns.Add("colA", System.Type.GetType("System.Int32"));
DataTable3.Columns.Add("colB", System.Type.GetType("System.String"));
DataTable3.Columns.Add("colC", System.Type.GetType("System.String"));
DataTable3.Columns.Add("colD", System.Type.GetType("System.String"));

按照 Tim 的建议,我使用 foreach 循环存储了“查询”的结果:

foreach (var x in query)
{
    DataRow newRow = DataTable3.NewRow();
    newRow.SetField("colA", x.col1A);
    newRow.SetField("colB", x.col1B);
    newRow.SetField("colC", x.col1C);
    newRow.SetField("colD", x.col2B);
    DataTable3.Rows.Add(newRow);
}

这可以通过一些额外的努力以几种方式通用化,但这对于我的目的来说已经足够了。

【问题讨论】:

  • +1 很高兴看到“向我发送工作代码”或“向我发送解释...的链接”以外的问题

标签: c# linq join datarow anonymous


【解决方案1】:

Linq 应该只用于查询。使用它来查询源并构建您的匿名类型。然后使用foreach 添加DataRows。一般来说:您不能将CopyToDataTable 用于IEnumerable&lt;DataRow&gt; 之外的任何其他内容。这就是匿名类型错误的原因。

(有一种使用反射的解决方法,我一般不会建议:

Implement CopyToDataTable&lt;T&gt; Where the Generic Type T Is Not a DataRow)

恕我直言,foreach 是你能做的最好的,因为它非常易读:

var query =
    from dt1 in DataTable1.AsEnumerable()
    join dt2 in DataTable2.AsEnumerable()
        on dt1.Field<string>("col1D") equals dt2.Field<string>("col2A")
    select new
    {
        col1A = dt1.Field<int>("col1A"),
        col1B = dt1.Field<string>("col1B"),
        col1C = dt1.Field<string>("col1C"),
        col2B = dt2.Field<string>("col2B")
    };

foreach (var x in query)
{
    DataRow newRow = DataTable3.Rows.Add();
    newRow.SetField("col1A", x.col1A);
    newRow.SetField("col1B", x.col1B);
    newRow.SetField("col1C", x.col1C);
    newRow.SetField("col2B", x.col2B);
}

【讨论】:

  • 数据正在进入 newRow 对象,但它没有填充 DataTable3... 是否缺少某种操作来提交该行或将其重新分配回表中? ...刚刚测试,添加了它并且它有效!:DataTable3.Rows.Add(newRow);
  • @averyoo7:抱歉,将 DataTable.NewRow 与 DataTable.Rows.Add 混淆了。 (已编辑)当使用 NewRow 时,您必须通过 DataTable.Rows.Add(newRow) 将新行添加到表中。
  • 祝福你蒂姆!谢谢你。我是 Linq 的新手,还不爱,但至少它有效。我会用你的新信息编辑我原来的问题。
  • 我认为这个答案可能描述的是内连接而不是左连接?
【解决方案2】:

我相信您可以按如下方式使用 CopyToDataTable:

dt1.AsEnumerable()
    .Join(dt2.AsEnumerable(),
        d => d.Field<string>("col1D"),
        d => d.Field<string>("col2A"),
        (d1, d2) => new { Dt1 = d1, Dt2 = d2 })
    .Select(a => {
            DataRow newRow = dt3.NewRow();
            newRow.SetField("colA", a.Dt1.Field<int>("col1A"));
            newRow.SetField("colB", a.Dt1.Field<string>("col1B"));
            newRow.SetField("colC", a.Dt1.Field<string>("col1C"));
            newRow.SetField("colD", a.Dt2.Field<string>("col2B"));
            return newRow;
        })
    .CopyToDataTable(dt3, LoadOption.OverwriteChanges);

请注意,这是按照您的要求执行内连接而不是左连接。

如果您真的想要左连接,我认为以下满足您的要求:

dt1.AsEnumerable()
    .GroupJoin(dt2.AsEnumerable(),
        d1 => d1.Field<int>("col1D"),
        d2 => d2.Field<int>("col2A"),
        (l, r) => new { Left = l, Right = r })
    .Select(anon => {
            if(anon.Right.Any())
            {
                return anon.Right.Select(r =>
                {
                    var newRow = dt3.NewRow();
                    newRow.SetField("colA", anon.Left.Field<int>("col1A"));
                    newRow.SetField("colB", anon.Left.Field<string>("col1B"));
                    newRow.SetField("colC", anon.Left.Field<string>("col1C"));
                    newRow.SetField("colD", r.Field<string>("col2B"));
                    return newRow;
                });
            }
            else
            {
                var newRow = dt3.NewRow();
                newRow.SetField("colA", anon.Left.Field<int>("col1A"));
                newRow.SetField("colB", anon.Left.Field<string>("col1B"));
                newRow.SetField("colC", anon.Left.Field<string>("col1C"));
                return new DataRow[] { newRow };
            }
        })
    .Aggregate((accum, next) => accum.Union(next))
    .CopyToDataTable(dt3, LoadOption.OverwriteChanges);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多