代码之家  ›  专栏  ›  技术社区  ›  Peter

如何在实体框架核心2.0上使用lambda语法在LINQ中实现左外连接?

  •  4
  • Peter  · 技术社区  · 7 年前

    我试图在Linq中针对实体框架核心2.0 DbContext实现一个左外连接。重要的是将查询转换为SQL,而不是在本地进行计算。我回顾了几个StackOverflow解决方案,包括 this one 这很好,但没有人使用EF Core。

    我遇到的问题是,EF Core为 DefaultIfEmpty() 方法:

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

    没有 DefaultIfEmpty() 方法使用内部联接。我的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
                               });
    

    有人知道怎么解决这个问题吗?

    1 回复  |  直到 7 年前
        1
  •  11
  •   Peter    7 年前

    好吧,这是我的错误,根据另一个SO问题中的评论,注意到 DefaultIfEmpty() 是使查询成为外部联接所必需的。查看底层SQL,左连接 正在提交 当我删除 DefaultIfEmpty() 规格我不确定这是否与在内存集合中执行左连接不同,但它解决了我的问题。

    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将生成左连接。 使用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))
    

    请注意 DefaultIfEmpty() 从LINQ查询得到内部联接。