简体   繁体   English

如何通过自定义连接将此Linq查询转换为DataSet?

[英]How do I get this Linq query with custom join into a DataSet?

I cannot seem to get past the anonymous type used by Linq's select clause when using join. 在使用join时,我似乎无法通过Linq的select子句使用的匿名类型。 I want to put my query result into a datatable. 我想将查询结果放入数据表中。 Here's what I have in memory at runtime... 这是我在运行时的内存中...

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>

Here's what I want (SQL equivelant to what I'm looking for)... 这就是我想要的(SQL equivelant到我正在寻找的)......

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)

Working backwards, I see this simple line to place an enumerated query result set into a datatable: 向后工作,我看到这个简单的行将枚举查询结果集放入数据表中:

DataTable DataTable3 = query.CopyToDataTable();

Here's what I tried... I don't know how to build 'query' in a way that will allow me to use this. 这是我尝试过的...我不知道如何以允许我使用它的方式构建'查询'。 Attempt 1 (using var query; this worked, read further): 尝试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") };

But that gives me this error on 'query.CopyToDataTable()': 但是这给了我'query.CopyToDataTable()'这个错误:

"Error 1 The type 'AnonymousType#1' cannot be used as type parameter 'T' in the generic type or method 'System.Data.DataTableExtensions.CopyToDataTable(System.Collections.Generic.IEnumerable)'. There is no implicit reference conversion from 'AnonymousType#1' to 'System.Data.DataRow'." “错误1类型'AnonymousType#1'不能用作泛型类型或方法'System.Data.DataTableExtensions.CopyToDataTable(System.Collections.Generic.IEnumerable)'中的类型参数'T'。没有隐式引用转换从'AnonymousType#1'到'System.Data.DataRow'。“

From what I can surmise, this error is because 'select new' doesn't build DataRows into 'query', which .CopyToDataTable() demands; 从我可以推测,这个错误是因为'select new'没有将DataRows构建成'query',.CopyToDataTable()要求; it just builds anonymous records. 它只是建立匿名记录。 Furthermore, I don't know how to just cast query elements into DataRow because "it is inaccessible due to protection level". 此外,我不知道如何将查询元素强制转换为DataRow,因为“由于保护级别而无法访问它”。

Attempt 2 (using IEnumerable query; don't bother trying this): 尝试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") };

This gives an error on 'join': 这会给'join'带来错误:

"Error 5 Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)" “错误5无法将类型'System.Collections.Generic.IEnumerable'隐式转换为'System.Collections.Generic.IEnumerable'。存在显式转换(您是否错过了转换?)”

On a side note, if I comment out the 'join' clause (and the corresponding dt2 column in the 'select'), I get this error on the 'select': 另外,如果我注释掉'join'子句(以及'select'中相应的dt2列),我会在'select'上看到这个错误:

"Error 2 Cannot implicitly convert type 'System.Data.EnumerableRowCollection' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?)" “错误2无法将类型'System.Data.EnumerableRowCollection'隐式转换为'System.Collections.Generic.IEnumerable'。存在显式转换(您是否错过了转换?)”

I also tried pre-defining DataTable3 with columns to give the 'select' clause a real DataRow to go off of, but I still couldn't get the 'select' to take advantage of it. 我还尝试使用列预定义DataTable3,使'select'子句成为一个真正的DataRow,但我仍然无法使用'select'来利用它。


Edits, Details, Solution: Thanks Tim for the help. 编辑,细节,解决方案:感谢蒂姆的帮助。 I went with Attempt 1 (var query). 我选择了尝试1(var查询)。 I also pre-defined DataTable3 as such: 我也预先定义了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"));

I stored the results from 'query' using a foreach loop as Tim suggested: 我使用foreach循环存储了来自'query'的结果,如Tim建议:

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

This could be genericized in a few ways with some additional effort, but this was good enough for my purposes. 这可以通过一些额外的努力在几个方面进行通用化,但这对我的目的来说已经足够了。

Linq should be used for queries only. Linq应仅用于查询。 Use it to query the source and build your anonymous types. 使用它来查询源并构建您的匿名类型。 Then use a foreach to add the DataRows . 然后使用foreach添加DataRows In general: you cannot use CopyToDataTable for anything else than IEnumerable<DataRow> . 通常:除了IEnumerable<DataRow>之外,您不能使用CopyToDataTable That's the reason for the error on the anonymous type. 这就是匿名类型错误的原因。

( There is a workaround using reflection that i wouldn't suggest in general: (有一种使用反射的解决方法,我一般不会建议:

Implement CopyToDataTable<T> Where the Generic Type T Is Not a DataRow ) 实现CopyToDataTable<T>通用类型T不是DataRow

Imho a foreach is the best you can do since it's very readable: Imho是一个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);
}

I believe you can utilize the CopyToDataTable as follows: 我相信您可以使用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);

Please note that this is performing an Inner Join rather than a Left Join as you requested. 请注意,这是根据您的要求执行内部联接而不是左联接。

If you really want a Left Join I think the following meets your requirements: 如果你真的想要一个Left Join,我认为以下内容符合你的要求:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM