【问题标题】:C# Entity Framework Core: Processing of the LINQ expression failedC# Entity Framework Core:处理 LINQ 表达式失败
【发布时间】:2020-10-22 08:39:56
【问题描述】:

情况

我们的 MySQL 数据库中有以下表格:

Table club(有更多列,但我们现在只关心 ID):

Id | ...
---+----
1  | ...

表格类型:

Id | GenreName
---+-----------
1  | Rock
2  | Classic
3  | Techno

和表club2genre:

ClubId | GenreId
-------+--------
1      | 1
1      | 2
1      | 3

MySQL 查询

使用以下 MySQL 查询:

SELECT
    club.Id,
    GROUP_CONCAT(genre.GenreName) as Music
FROM
    club
INNER JOIN club2genre
    ON club2genre.clubid = club.id
INNER JOIN genre
    ON genre.id = club2genre.genreid
GROUP BY
    club.Id;

我们得到(如预期的那样)这个结果:

Id | Music
---+---------
1  | Rock,Classic,Techno

有问题的(有问题的)C# LINQ 表达式

var queryResult = DbContext.Club.Join(
        DbContext.Club2Genre,
        club => club.Id,
        club2genre => club2genre.ClubId,
        (club, cg) => new
        {
            Id = club.Id,
            GenreId = cg.GenreId
        })
    .GroupJoin(
        DbContext.Genre,
        temp => temp.GenreId,
        genre => genre.Id,
        (temp, genreList) => new
        {
            ClubId = temp.Id,
            Genres = genreList.Select(genre => genre.Name)
        })
    .GroupBy(result => result.ClubId);

var result = result.ToList();

理论上,这个 Entity Framework Core LINQ 表达式应该是上面 MySQL 查询的 1:1 转换,其中别名为 Music(GROUP_CONCAT 之一)的结果列应该是 IEnumerable,但是在执行时会出现以下异常被抛出:

有趣的是,它只会在queryResult 上调用ToList() 时失败,所以我什至不确定 LINQ 表达式本身就是问题所在。而且(至少对我而言)错误消息似乎并没有特别有用,除了基本上说 "EF Core might be buggy but also maybe not. Who knows"。

所以我的问题是:

1.) 使用 LINQ 和 EF Core 是否可以实现我的目标?

2.) 如果是这样,我的代码有什么问题,为什么会崩溃?

3.) 我该如何解决?

【问题讨论】:

  • GroupJoin 只是不被支持,看这个问题:github.com/dotnet/efcore/issues/17068#issuecomment-586464350
  • Ooof :/ 我想这解释了为什么它不起作用。有 *supported *alternative 吗?
  • 我个人不知道。我记得,即使 String.Join 在 EF Core 中也不受支持,但您使用的特定提供程序可能支持它......
  • 好吧,我必须将我的查询拆分为多个子查询,然后在 c# 中执行组加入逻辑。不过,谢谢你调查它。

标签: c# mysql entity-framework linq


【解决方案1】:

使用扩展函数,您可以生成 LEFT JOIN 并创建一个展平结果,然后可以在客户端对它进行分组以模拟 GroupJoin。

我假设返回左侧多个副本的单个查询比拆分左侧和右侧的多个查询更可取,因为这就是 LINQ to SQL 所做的,但也可以实现一个翻译两个查询,一个用于左侧,一个用于与左侧匹配的右侧,然后加入客户端。这以两个查询为代价消除了重复的左侧。您还可以通过输入Selects 将左侧的重复和网络流量降至最低,这会将每一侧缩小到仅键和结果所需的列。

public static class QueryableExt {
    // implement GroupJoin for EF Core 3 by using `LEFT JOIN` and grouping on client side
    public static IEnumerable<TRes> GroupJoinEF<TLeft,TRight,TKey,TRes>(
        this IQueryable<TLeft> leftq,
             IQueryable<TRight> rightq,
             Expression<Func<TLeft,TKey>> leftKeyExp,
             Expression<Func<TRight,TKey>> rightKeyExp,
             Func<TLeft, IEnumerable<TRight>, TRes> resFn) =>

        leftq.GroupJoin(rightq, leftKeyExp, rightKeyExp, (left, rightj) => new { left, rightj })
             .SelectMany(lrj => lrj.rightj.DefaultIfEmpty(), (lrj, right) => new { lrj.left, right })
             .AsEnumerable()
             .GroupBy(lr => lr.left, lr => lr.right, (left, rightg) => resFn(left, rightg));
}

使用此扩展程序,您的查询变为:

var queryResult = DbContext.Club
                    .GroupJoinEF(DbContext.Club2Genre.Join(DbContext.Genre, c2g => c2g.GenreId, g => g.Id, (c2g, g) => new { c2g.Id, g.Genre }),
                         c => c.Id,
                         cg => cg.Id,
                         (c, cgj) => new {
                             c.Id,
                             Music = String.Join(",", cgj.Select(cg => cg?.Genre))
                         }
                    );

但是,由于您没有在查询中使用 LEFT JOIN,因此您不需要完整的 GroupJoin 实现,可以只使用 Join 并再次在客户端分组:

var queryResult = DbContext.Club
                    .Join(DbContext.Club2Genre, c => c.Id, c2g => c2g.Id, (c, c2g) => new { c.Id, c2g.GenreId })
                    .Join(DbContext.Genre, cgi => cgi.GenreId, g => g.Id, (cgi, g) => new { cgi.Id, g.Genre })
                    .AsEnumerable()
                    .GroupBy(cg => cg.Id, (cId, cgg) => new { Id = cId, Music = String.Join(",", cgg.Select(cg => cg.Genre)) });

【讨论】:

    猜你喜欢
    • 2021-08-24
    • 1970-01-01
    • 2020-03-23
    • 2020-05-29
    • 1970-01-01
    • 2021-02-11
    • 2020-12-06
    • 1970-01-01
    • 2022-06-10
    相关资源
    最近更新 更多