【问题标题】:Getting counts of LEFT JOINED entities via group by通过 group by 获取 LEFT JOINED 实体的计数
【发布时间】:2019-07-22 17:04:43
【问题描述】:

考虑以下查询,该查询必须返回导演以及每部电影的电影数量和演员数量。如何使用 EF CORE 2.2 在 LINQ 中编写此代码?我需要 LINQ 来生成实际使用 GROUP BY 和 EF CORE 2.1 和转发中支持的 SQL 聚合的 SQL。

SELECT DirectorName, COUNT(m.MovieID), COUNT(a.ActorID)
FROM Directors d
LEFT OUTER JOIN Movies AS m ON m.DirectorID = d.DirectorID
LEFT OUTER JOIN Actors AS a ON a.MovieID = m.MovieID
WHERE d.DirectorID = 1
GROUP BY DirectorName

示例 LINQ 查询是基于答案构建的,并且确实给了我结果,但该查询只有一个 LEFT 联接,没有分组依据。输出表明 DefaultIfEmpty()、GroupBy 和 Count 无法翻译,将在本地进行评估。

var results =
(
    from d in _moviesContext.Directors
    join m in _moviesContext.Movies on d.DirectorId equals m.DirectorId 
        into grpM from movies in grpM.DefaultIfEmpty()
    join a in _moviesContext.Actors on movies.MovieId equals a.MovieId
        into grpA from actors in grpA.DefaultIfEmpty()
    where d.DirectorId == 1
    group new { d, grpM, grpA } by new
    {
        d.DirectorName
    } into grp
    select new
    {
        DirectoryName = grp.Key.DirectorName,
        MovieCount = grp.Sum(g => g.grpM.Count()),
        ActorAcount = grp.Sum(g => g.grpA.Count())
    }
).ToList();

LINQ 表达式 'DefaultIfEmpty()' 无法翻译,将被评估 本地。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 无法翻译表达式“DefaultIfEmpty()”并将 当地评价。 Microsoft.EntityFrameworkCore.Query:警告: LINQ 表达式“来自 {[grpM] => DefaultIfEmpty()} 中的电影电影” 无法翻译,将在当地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'GroupBy(new f__AnonymousType81(DirectorName = [d].DirectorName), new <>f__AnonymousType62(d = [d], grpM = [grpM]))' 不能 翻译并将在当地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'DefaultIfEmpty()' 无法翻译,将被评估 本地。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 无法翻译表达式“DefaultIfEmpty()”并将 当地评价。 Microsoft.EntityFrameworkCore.Query:警告: LINQ 表达式“来自 {[grpM] => DefaultIfEmpty()} 中的电影电影” 无法翻译,将在当地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'GroupBy(new f__AnonymousType81(DirectorName = [d].DirectorName), new <>f__AnonymousType62(d = [d], grpM = [grpM]))' 不能 翻译并将在当地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'Count()' 无法翻译,将在本地进行评估。 Microsoft.EntityFrameworkCore.Query:警告:LINQ 表达式 'Sum()' 无法翻译,将在本地计算。

这是模型

public partial class Directors
{
    public int DirectorId { get; set; }
    public string DirectorName { get; set; }
}

public partial class Movies
{
    public int MovieId { get; set; }
    public string MovieName { get; set; }
    public int? DirectorId { get; set; }
}

public partial class Actors
{
    public int ActorId { get; set; }
    public string ActorName { get; set; }
    public int? MovieId { get; set; }
}

【问题讨论】:

  • LEFT OUTER JOIN in LINQ的可能重复
  • @Geekn 请包括查询中使用的实体模型。否则,我们对您的代码无能为力。
  • 对不起...我知道原来的帖子有点多。我已经更新了它,我认为是一个更简单的问题。
  • 原帖至少展示了您自己的努力,这表明您陷入了将 SQL 连接转换为 LINQ 连接的臭名昭著的陷阱。一个提示:使用导航属性,这一切都会容易得多。
  • 可以编写翻译接近 SQL 查询的 LINQ 查询。但请注意,SQL COUNT(MovieId) 不是您可能想的那样 - 它基本上是 COUNT(*) 忽略了外部连接中的 NULL 值。但是加入 Actors(1 到多个)将多次计算电影,因此 MovieCount 无效。您可能需要的是 SQL COUNT(DISTINCT MovieId),但它没有 LINQ 等效项,因此没有 EF Core 翻译。一般来说,EF Core 还没有为复杂的分组/聚合查询做好准备。

标签: c# linq entity-framework-core ef-core-2.2


【解决方案1】:

我不确定为什么当 Actor 通常有一个电影列表时,为什么你的 Actors 只有一个电影 ID 的 int 但我相信你需要在你的组中包含所有三种对象类型,然后选择不同的电影和不同的演员来得到计数。

例子:

var directors = new[] { new { DirectorName = "Director A", DirectorID = 1 },
                        new { DirectorName = "Director B", DirectorID = 2 }};
var movies = new[] { new { MovieName = "Movie A", MovieID = 1, DirectorID = 1 },
                     new { MovieName = "Movie B", MovieID = 2, DirectorID = 2 }};
var actors = new[] { new { ActorName = "Actor A", ActorID = 1, MovieID = 1},
                     new { ActorName = "Actor B", ActorID = 2, MovieID = 1},
                     new { ActorName = "Actor C", ActorID = 3, MovieID = 1},
                     new { ActorName = "Actor D", ActorID = 4, MovieID = 2}};

var results = from d in directors
              from m in movies
                .Where(m => m.DirectorID == d.DirectorID)
              from a in actors
                .Where(a => a.MovieID == m.MovieID)
              where d.DirectorID == 1
              group new { d, m, a } by d.DirectorName into grp
              select new 
              { DirectorName = grp.Key,
                MovieCount = grp.Select(x => x.m).Distinct().Count(),
                ActorCount = grp.Select(x => x.a).Distinct().Count()
              };

会收获

result = new [] { new { DirectorName = "Director A", MovieCount = 1, ActorCount = 3}};

【讨论】:

  • 这确实给了我结果,但是,当我使用这种方法时,SELECT 语句包含所有表中的所有列,这并不是我真正需要的。我需要 SQL 输出来使用基于 GROUP by 子句的 COUNT()。这是该语句的输出。 SELECT [d].[DirectorID], [d].[DirectorName], [m].[MovieID], [m].[DirectorID], ... FROM [Directors] AS [d] INNER JOIN [Movies] AS [m ] ON [d].[DirectorID] = [m].[DirectorID] INNER JOIN [Actors] AS [a] ON [m].[MovieID] = [a].[MovieID] WHERE [d].[DirectorID] = 1 个订单由 [d].[DirectorName]
  • 你的最终目标是什么?您是否尝试使用 Linq 生成 SQL?我的回答基于如何在 Linq 中编写“SELECT DirectorName, COUNT(m.MovieID), COUNT(a.ActorID)”。最后,您会获得一系列导演姓名以及他们的电影和演员人数。
  • 你是正确的,它提供了答案,但我需要 LINQ 从问题中生成 SQL。我真的希望生成的 SQL 只返回显示所需的数据(如果这有任何意义,不是所有表中涉及的所有表的所有数据。EF CORE 2.1 支持将 LINQ 转换为真正的 GROUP BY 子句并允许使用要在 SQL 上执行的 SQL 聚合(例如 SUM、AVG 等)。这确实是这里的目标。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-04
  • 2017-01-10
  • 2016-04-27
  • 1970-01-01
  • 2013-11-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多