繁体   English   中英

C# 实体框架核心:处理 LINQ 表达式失败

[英]C# Entity Framework Core: Processing of the LINQ expression failed

情况

我们的 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 转换,其中别名为MusicGROUP_CONCAT )的结果列应该是一个 IEnumerable 但是在执行时抛出以下异常:

错误

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

所以我的问题是:

1.) 我正在尝试使用 LINQ 和 EF Core 做什至可能吗?

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

3.) 我该如何解决?

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

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

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和 group 即可:

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

暂无
暂无

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

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