【问题标题】:left join in Linq queryLinq查询中的左连接
【发布时间】:2015-01-27 23:51:46
【问题描述】:

我正在尝试在 linq 查询中进行左连接,而不是内连接。我找到了与使用 DefaultIfEmpty() 相关的答案,但我似乎无法让它发挥作用。以下是 linq 查询:

from a in dc.Table1 
join e in dc.Table2 on a.Table1_id equals e.Table2_id
where a.Table1_id == id
orderby a.sort descending
group e by new
{
    a.Field1,
    a.Field2
} into ga
select new MyObject
{
    field1= ga.Key.Field1,
    field2= ga.Key.Field2,
    manySubObjects = (from g in ga select new SubObject{
                                                        fielda= g.fielda,
                                                        fieldb= g.fieldb
                                                        }).ToList()
}).ToList();

该查询只为我提供表 1 中在表 2 中具有相应记录的行。我希望将表 1 中的每条记录填充到 MyObject 中,并在 manySubObjects 中为每个 MyObject 列出一个 0-n 对应记录的列表。

更新: 我尝试了下面提到的“可能重复”问题的答案。我现在有以下代码,即使没有 Table2 记录,也会为 Table1 中的每个项目提供一条记录。

from a in dc.Table1 
join e in dc.Table2 on a.Table1_id equals e.Table2_id into j1
from j2 in j1.DefaultIfEmpty()
where a.Table1_id == id
orderby a.sort descending
group j2 by new
{
    a.Field1,
    a.Field2
} into ga
select new MyObject
{
    field1= ga.Key.Field1,
    field2= ga.Key.Field2,
    manySubObjects = (from g in ga select new SubObject{
                                                        fielda= g.fielda,
                                                        fieldb= g.fieldb
                                                        }).ToList()
}).ToList();

但是,使用此代码,当 table2 中没有记录时,我将“manySubObject”作为一个列表,其中有一个“SubObject”,其中“SubObject”的属性的所有值为空。如果 table2 中没有值,我真正想要的是“manySubObjects”为空。

【问题讨论】:

  • 当 table2 中没有值时我应该添加 manySubObjects 应该为空。我在上面的问题中进行了尝试,结果包含所有记录,所以这是一个改进,但是当 table2 中没有记录时,我得到一个实例化的子对象,其中包含所有空值。如果 table2 中没有值,我真正想要的是“manySubObjects”为空。
  • 我认为我的答案中的三元组解决了您最近关于需要“manySubObjects”为空的评论。

标签: c# linq entity-framework join left-join


【解决方案1】:

回复您的更新,要创建空列表,您可以在 manySubObjects 的赋值中执行三元组。

select new MyObject
{
    field1= ga.Key.Field1,
    field2= ga.Key.Field2,
    manySubObjects =
        (from g in ga select g).FirstOrDefaut() == null ? null : 
        (from g in ga select new SubObject {
           fielda= g.fielda,
           fieldb= g.fieldb
        }).ToList()
}).ToList();

回复您的 cmets,以上内容适用于 Linq to Objects,但不适用于 Linq to SQL。 Linq to SQL 会抱怨它,“无法将表达式...转换为 SQL 并且无法将其视为本地表达式。”这是因为 Linq 无法将自定义 new SubObject 构造函数转换为 SQL。为此,您必须编写更多代码来支持转换为 SQL。请参阅Custom Method in LINQ to SQL querythis article

我认为我们已经充分回答了您关于左连接的原始问题。考虑提出一个关于在 Linq to SQL 查询中使用自定义方法/构造函数的新问题。

【讨论】:

  • 我试过这个,但是,我收到一个错误,说Could not translate expression "ga.Select(g = > new SubObject(){fielda = g.fielda, fieldb = g.fieldb})" into SQL and could not treat as a local expression。知道什么可能导致此错误吗?我找不到我的代码与您的代码有什么不同...
  • @kralco626 可能是您代码的= > 部分中的等号和尖括号之间有一个空格。
  • 我的代码中实际上根本没有“= >”。它一定是把我的代码编译成那种格式?
  • 在您的回答中,如果我注释掉 (from g in ga select g).FirstOrDefaut() == null ? null : 行,错误就会消失(显然,查询不会按我想要的方式运行)。所以这条线的构造方式不喜欢......
  • @kralco626 请发表评论,链接到您的代码的dotnetfiddle。这样我就可以看你的代码了。这样更容易。
【解决方案2】:

我认为你想要的结果可以通过使用GroupJoin()来给出

下面的代码会产生这样的结构

Field1, Field2, List 如果为空则为空

示例代码

 var query = dc.Table1.Where(x => Table1_id == id).OrderBy(x => x.sort)
                .GroupJoin(dc.Table2, (table1 => table1.Table1_id), (table2 => table2.Table2_id),
                    (table1, table2) => new MyObject 
                                        { 
                                            field1 = table1.Field1, 
                                            field2 = table1.Field2, 
                                            manySubObjects = (table2.Count() > 0) 
                                                ? (from t in table2 select new SubObject { fielda = t.fielda, fieldb = t.fieldb}).ToList()
                                                : null 
                                        }).ToList();

Dotnetfiddle link

更新

从你的评论中我看到了这个

ga.Select(g = > new SubObject(){fielda = g.fielda, fieldb = g.fieldb})

我认为应该是(取决于“ga”是如何构建的)

ga.Select(g => new SubObject {fielda = g.fielda, fieldb = g.fieldb})

请用整个查询更新您的问题,这将有助于解决问题。

** 更新 BIS **

 sentEmails = //ga.Count() < 1 ? null :
              //(from g in ga select g).FirstOrDefault() == null ? null :
             (from g in ga select new Email{
                  email_to = g.email_to,
                  email_from = g.email_from,
                  email_cc = g.email_cc,
                  email_bcc = g.email_bcc,
                  email_subject = g.email_subject,
                  email_body = g.email_body }).ToList()

应该是:

 sentEmails = //ga.Count() < 1 ? null :
             ((from g in ga select g).FirstOrDefault() == null) ? null :
             (from g in ga select new Email{
                  email_to = g.email_to,
                  email_from = g.email_from,
                  email_cc = g.email_cc,
                  email_bcc = g.email_bcc,
                  email_subject = g.email_subject,
                  email_body = g.email_body }).ToList()

检查组是否有 First,如果没有,则该组没有任何记录,因此时间戳的 Action.Name 没有要发送的电子邮件。如果 First 不为空,则循环抛出组元素并创建电子邮件列表,

【讨论】:

  • 谢谢。 GroupJoin() 确实可以进行更整洁的查询。
  • 我得到了与你的答案相同的Could not translate expression "ga.Select(g = &gt; new SubObject(){fielda = g.fielda, fieldb = g.fieldb})" into SQL and could not treat as a local expression 异常,我得到了肖恩的答案。我看不出我所做的与你所做的不同......
  • 用您的查询副本更新您的问题
  • 为什么要检查组中的第一个元素是否为空?如果您的组中没有任何项目,它将如何创建电子邮件实体
【解决方案3】:
var results =
(
    // Use from, from like so for the left join:
    from a in dc.Table1
    from e in dc.Table2
        // Join condition goes here
        .Where(a.Id == e.Id)
        // This is for the left join
        .DefaultIfEmpty()
    // Non-join conditions here
    where a.Id == id
    // Then group
    group by new
    {
        a.Field1,
        a.Field2
    }
).Select(g =>
    // Sort items within groups
    g.OrderBy(item => item.sortField)
    // Project required data only from each item
    .Select(item => new
    {
        item.FieldA,
        item.FieldB
    }))
// Bring into memory
.ToList();

然后将内存中投影到您的非 EF 模型类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-13
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 2016-02-17
    相关资源
    最近更新 更多