如何使用 Entity Framework Core 2.0 上的 lambda 语法在 LINQ 中实现 LEFT OUTER JOIN?

Posted

技术标签:

【中文标题】如何使用 Entity Framework Core 2.0 上的 lambda 语法在 LINQ 中实现 LEFT OUTER JOIN?【英文标题】:How can I implement a LEFT OUTER JOIN in LINQ using lambda syntax on Entity Framework Core 2.0? 【发布时间】:2018-09-08 05:19:49 【问题描述】:

我正在尝试在 Linq 中针对 Entity Framework Core 2.0 DbContext 实现 LEFT OUTER JOIN。将查询转换为 SQL,而不是在本地计算,这一点很重要。我查看了几个 *** 解决方案,包括 this one,这很好,但没有一个使用 EF Core。

我遇到的问题是 EF Core 为 DefaultIfEmpty() 方法返回以下警告/错误:

The LINQ expression 'DefaultIfEmpty()' could not be translated and will be evaluated locally

如果没有 DefaultIfEmpty() 方法,则使用 INNER JOIN。我的 LINQ 查询如下所示:

var join = context.Portfolios
           .Where(p => p.IsActive)
           .GroupJoin(context.BankAccounts, 
                      prt => prt.Id, 
                      bnk => bnk.PortfolioId, 
                      (prt, bnks) => new Portfolio=prt,Account=bnks.DefaultIfEmpty())
           .SelectMany(r => r.Accounts.DefaultIfEmpty(),
                       (p, b) => new 
                           
                               Id = p.Portfolio.Id,
                               BankAccount = b.BankAccountNumber,
                               BankRef = b.BeneficiaryReference,
                               Code = p.Portfolio.Code,
                               Description = p.Portfolio.DisplayName
                           );

有人知道解决这个问题的方法吗?

【问题讨论】:

不确定这个问题的价值是什么。 left LINQ 中的连接始终使用链接中的模式执行(您没有正确应用 - 应该只有一个 DefaultIfEmpty(),而不是两个)。而在 EF (Core) 中,首选方式是根本不使用连接,而是使用导航属性。 感谢 Ivan,我会尝试其他选项,看看能否将其用作 LEFT JOIN。 【参考方案1】:

好的,这是我的错误,基于另一个 SO 问题中的评论指出 DefaultIfEmpty() 是使查询成为 OUTER JOIN 所必需的。查看底层 SQL,当我删除 DefaultIfEmpty() 规范时,正在向数据库提交 LEFT JOIN 。我不确定这是否与对内存中的集合执行 LEFT JOIN 不同,但它解决了我的问题。

EF Core 为此 Linq 查询生成的 SQL 如下所示:

SELECT [p].[ID], 
       [bnk].[BankAccountNumber] AS [BankAccount], 
       [bnk].[BeneficiaryReference] AS [BankRef], 
       [p].[Code], 
       [p].[DisplayName] AS [Description]
    FROM [Portfolio] AS [p]
    LEFT JOIN [BankAccount] AS [bnk] ON [p].[ID] = [bnk].[PortfolioId]
WHERE (([p].[IsActive] = 1)))

编辑: 找到时间对此进行测试,@Ivan Stoev 是正确的:如果您的导航属性在 EF 上下文定义中正确设置,EF 将生成 LEFT JOIN。 这是使用 EF 时更好的方法。

投资组合上的 EF 导航属性:

public virtual ICollection<BankAccount> BankAccounts  get; set; 

通过导航属性查询 LINQ:

var join = context.Portfolios
                  .Where(p => p.IsActive)
                  .SelectMany(p => p.BankAccounts.DefaultIfEmpty(), (p, b) => new
                                                
                                                    Id = p.Id,
                                                    BankAccount = b.BankAccountNumber,
                                                    BankRef = b.BeneficiaryReference,
                                                    Code = p.Code,
                                                    Description = p.DisplayName
                                                );

生成的 SQL 代码:

SELECT [p].[ID], [p.BankAccounts].[BankAccountNumber] AS [BankAccount], [p.BankAccounts].[BeneficiaryReference] AS [BankRef], [p].[Code], [p].[DisplayName] AS [Description]
    FROM [core].[Portfolio] AS [p]
    LEFT JOIN [ims].[BankAccount] AS [p.BankAccounts] ON [p].[ID] = [p.BankAccounts].[PortfolioId]
WHERE (([p].[IsActive] = 1))

请注意,从 LINQ 查询中删除 DefaultIfEmpty() 会导致 INNER JOIN。

【讨论】:

以上是关于如何使用 Entity Framework Core 2.0 上的 lambda 语法在 LINQ 中实现 LEFT OUTER JOIN?的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 学习系列 - 认识理解Entity Framework

如何使用 Entity Framework Core 模拟异步存储库

如何使用 Entity Framework 生成和自动递增 Id

如何使用 Entity Framework Core 正确保存 DateTime?

如何使用 Entity Framework Core 获取主键值

如何在 Entity Framework 6 中使用 DbDataReader 获取表名?