【问题标题】:How can I use Linq expression for Join with GroupBy in EF Core 3.1如何在 EF Core 3.1 中使用 Linq 表达式与 GroupBy 进行联接
【发布时间】:2021-07-30 23:19:27
【问题描述】:

这是我的 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)
  }

当我在 EF Core 中使用查询时,我得到了这个异常:

LINQ 表达式'(GroupByShaperExpression: KeySelector: (j.AccountId), 元素选择器:(实体形状表达式: 实体类型:JournalVoucherLine 值缓冲区表达式: (ProjectionBindingExpression:EmptyProjectionMember) IsNullable: 假 ) ).FirstOrDefault()'
无法翻译。以可翻译的形式重写查询,或通过插入对 AsEnumerable()、AsAsyncEnumerable()、ToList() 或 ToListAsync() 的调用显式切换到客户端评估。请参阅https://go.microsoft.com/fwlink/?linkid=2101038 了解更多信息。 "

此查询的最佳做法是什么?

【问题讨论】:

    标签: linq entity-framework-core eager-loading ef-core-3.1


    【解决方案1】:

    首先,不要在GroupBy 结果上使用FirstOrDefault() 之类的方法 - 它们不可翻译。仅使用键和聚合函数(因为这是 SQL GROUP BY 运算符支持的)。

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

    例如

    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
    };
    

    成功转化为

    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]
    

    更新:使用方法语法的相同 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
        });
    

    但是如果你需要更多的连接,查询语法是更可取的。

    【讨论】:

    • 你好,Ivan 你能用 LINQ 方法语法写这个查询吗?
    • 嗨@ArminShoeibi,我为什么要这样做?有很多免费工具(甚至调试视图)可以向您展示等效的方法语法。或者你可以很容易地做到这一点,因为没有什么不寻常的,例如JournalVoucherLines.GroupBy(v =&gt; ...).Select(vg =&gt; new { ...}).Join(Parties, vg =&gt; ..., p =&gt; ..., (vg, p) =&gt; new { ...})
    • 谢谢,我应该搜索什么工具?
    • @Shahram 给你。
    • 但是如果你的意思是这个stackoverflow.com/questions/67474969/…,不,导航属性Voucher基本上是一个Join,因此不能在GroupBy内部或之后使用——你应该在适当的位置添加另一个手动连接该属性,再次,之后 group by select into。 EF Core 限制,抱歉。
    猜你喜欢
    • 2021-08-30
    • 2020-09-10
    • 2021-08-22
    • 2020-05-14
    • 2020-11-21
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多