如何在 EF Core 3.1 中使用 Linq 表达式与 GroupBy 进行连接

Sha*_*ram 3 linq eager-loading entity-framework-core ef-core-3.1

这是我的 SQL 查询,我在 linqpad 中测试了它,它有效,但它在 EF Core 3.1 中不起作用:

  from v in JournalVoucherLines
  group v by v.AccountId into vg
  join bp in Parties on vg.FirstOrDefault().AccountId equals bp.AccountId
  select new
  {
    name = bp.FullName,
    key = vg.Key,
    Creditor = vg.Sum(v => v.Credit),
    Deptor = vg.Sum(v => v.Debit),
    RemainAmount = vg.Sum(v => v.Credit) - vg.Sum(v => v.Debit)
  }
Run Code Online (Sandbox Code Playgroud)

当我在 EF Core 中使用查询时,出现以下异常:


无法翻译LINQ 表达式“(GroupByShaperExpression: KeySelector: (j.AccountId), ElementSelector:(EntityShaperExpression: EntityType: JournalVoucherLine ValueBufferExpression: (ProjectionBindingExpression: EmptyProjectionMember) IsNullable: False ) ).FirstOrDefault()”。以可翻译的形式重写查询,或者通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用来显式切换到客户端计算。有关详细信息,请参阅https://go.microsoft.com/fwlink/?linkid=2101038 。

此查询的最佳实践是什么?

Iva*_*oev 5

首先,不要使用像FirstOrDefault()on GroupByresult 这样的方法 - 它们是不可翻译的。仅使用键和聚合函数(因为这是 SQLGROUP BY运算符支持的功能)。

其次,对包含所需键/聚合的结果使用临时投影 ( Select) ,然后将其连接到另一个实体(表)以获取最终投影所需的附加信息。GroupBy

例如

from v in JournalVoucherLines
group v by v.AccountId into vg
select new // <-- temporary projection with group by fields needed
{
    AccountId = vg.Key,
    Credit = vg.Sum(v => v.Credit),
    Debit = vg.Sum(v => v.Debit),
} into vg
join bp in Parties on vg.AccountId equals bp.AccountId // <-- additional join(s)
select new
{
    name = bp.FullName,
    key = vg.AccountId,
    Creditor = vg.Credit,
    Deptor = vg.Debit,
    RemainAmount = vg.Credit - vg.Debit
};
Run Code Online (Sandbox Code Playgroud)

成功翻译为

SELECT [p].[FullName] AS [name], [t].[AccountId] AS [key], [t].[c] AS [Creditor], [t].[c0] AS [Deptor], [t].[c] - [t].[c0] AS [RemainAmount]
FROM (
    SELECT [j].[AccountId], SUM([j].[Credit]) AS [c], SUM([j].[Debit]) AS [c0]
    FROM [JournalVoucherLines] AS [j]
    GROUP BY [j].[AccountId]
) AS [t]
INNER JOIN [Parties] AS [p] ON [t].[AccountId] = [p].[AccountId]
Run Code Online (Sandbox Code Playgroud)

更新:使用方法语法的相同 LINQ 查询甚至非常简单:

var query = JournalVoucherLines
    .GroupBy(v => v.AccountId)
    .Select(vg => new
    {
        AccountId = vg.Key,
        Credit = vg.Sum(v => v.Credit),
        Debit = vg.Sum(v => v.Debit),
    })
    .Join(Parties, vg => vg.AccountId, bp => bp.AccountId, (vg, bp) => new
    {
        name = bp.FullName,
        key = vg.AccountId,
        Creditor = vg.Credit,
        Deptor = vg.Debit,
        RemainAmount = vg.Credit - vg.Debit
    });
Run Code Online (Sandbox Code Playgroud)

但如果您需要更多联接,查询语法是更好的选择。