简体   繁体   English

如何在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?

I am trying to implement a LEFT OUTER JOIN in Linq against an Entity Framework Core 2.0 DbContext. 我正在尝试针对实体框架核心2.0 DbContext在Linq中实现左外部联接。 It's important that the query is translated to SQL, rather than evaluated locally. 将查询转换为SQL,而不是在本地进行评估,这一点很重要。 I've reviewed several StackOverflow solutions including this one which is good, but none are using EF Core. 我已经查看了几种StackOverflow解决方案,其中包括一个不错的解决方案,但是没有一个使用EF Core。

The problem I get is that EF Core returns the following warning/error for the DefaultIfEmpty() method: 我得到的问题是EF Core为DefaultIfEmpty()方法返回以下警告/错误:

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

Without the DefaultIfEmpty() method an INNER JOIN is used. 如果没有DefaultIfEmpty()方法,则使用INNER JOIN。 My LINQ query looks like this: 我的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
                           });

Does anyone know a way around this? 有谁知道解决这个问题的方法吗?

OK, this is my mistake, based on a comment in another SO question that noted that DefaultIfEmpty() is necessary to make the query an OUTER JOIN. 好的,这是我的错误,基于另一个SO问题的注释,该注释指出DefaultIfEmpty()是使查询成为OUTER JOIN所必需的。 Looking at the underlying SQL, a LEFT JOIN is being submitted to the database when I remove the DefaultIfEmpty() specification. 查看基础SQL,当我删除DefaultIfEmpty()规范时, 正在向数据库提交 LEFT JOIN。 I'm not sure if this differs from doing a LEFT JOIN over in-memory collections, but it has solved my problem. 我不确定这是否不同于对内存中集合进行LEFT JOIN,但这已经解决了我的问题。

The SQL as generated by EF Core for this Linq query looks like this: 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)))

EDIT: Found time to test this out and @Ivan Stoev is correct: if your navigation properties are correctly setup in the EF context definition, EF will generate the LEFT JOIN. 编辑:找到时间进行测试,@ Ivan Stoev是正确的:如果在EF上下文定义中正确设置了导航属性,则EF将生成LEFT JOIN。 This is a better approach when using EF. 使用EF时,这是一种更好的方法。

EF navigation property on Portfolio: 投资组合上的EF导航属性:

public virtual ICollection<BankAccount> BankAccounts { get; set; }

LINQ query via navigation property: 通过导航属性进行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
                                                });

Resulting SQL code: 产生的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))

Note that dropping the DefaultIfEmpty() from the LINQ query results in an INNER JOIN. 请注意,从LINQ查询中删除DefaultIfEmpty()导致一个INNER JOIN。

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

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