我正在将一个EF6.x项目升级到EF Core 3.1。决定回归基础,重新按照如何从头开始设置关系的示例进行操作。
根据官方Microsoft文档EF Core Relationship Examples,我将示例翻译成了以下控制台应用程序:
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BlogPostsExample
{
class Program
{
async static Task Main(string[] args)
{
// SQL Running in a Docker container - update as required
var conString = "data source=localhost,14330;initial catalog=BlogsDb;persist security info=True;user id=sa;password=<Your super secure SA password>;MultipleActiveResultSets=True;App=EntityFramework;";
var ctx = new MyContext(conString);
await ctx.Database.EnsureCreatedAsync();
var result = await ctx.Posts.GroupBy(p => p.Blog).ToArrayAsync();
}
}
class MyContext : DbContext
{
private readonly string _connectionString;
public MyContext(string connectionString)
{
_connectionString = connectionString;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseSqlServer(_connectionString);
}
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId) //Tried with and without these keys defined.
.HasPrincipalKey(b => b.BlogId);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}
数据库中没有数据。EF Core 无法转换。
ctx.Posts.GroupBy(p => p.Blog)
转换为存储查询。这对我来说似乎是您可以尝试的最简单的GroupBy示例。
运行此代码时,您将获得以下异常:
System.InvalidOperationException: 'The LINQ expression 'DbSet<Post>
.Join(
outer: DbSet<Blog>,
inner: p => EF.Property<Nullable<int>>(p, "BlogId"),
outerKeySelector: b => EF.Property<Nullable<int>>(b, "BlogId"),
innerKeySelector: (o, i) => new TransparentIdentifier<Post, Blog>(
Outer = o,
Inner = i
))
.GroupBy(
source: p => p.Inner,
keySelector: p => p.Outer)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.'
唯一让它工作的方法是在
GroupBy
之前添加类似于AsEnumerable()的内容。从性能角度来看,这显然不是很好,它将分组操作转换为客户端操作,而您真正想做的是在服务器端进行分组。
我错过了什么重要的东西吗? 我很难相信EF Core不能像EF Framework自Day 1以来一样执行最简单的分组。 这似乎是任何数据驱动应用程序的基本要求? (或任何带有数据的应用程序!)
更新:
![enter image description here](https://istack.dev59.com/QH6rN.webp)
更新2:
如果您遵循JetBrains文章中的this,则可以执行此操作:
var ctx = new EntertainmentDbContext(conString);
await ctx.Database.EnsureCreatedAsync();
var dataTask = ctx
.Ratings
.GroupBy(x => x.Source)
.Select(x => new {Source = x.Key, Count = x.Count()})
.OrderByDescending(x => x.Count)
.ToListAsync();
var data = await dataTask;
但不是这样:
var ctx = new EntertainmentDbContext(conString);
await ctx.Database.EnsureCreatedAsync();
var dataTask = ctx
.Ratings
.GroupBy(x => x.Source)
// .Select(x => new {Source = x.Key, Count = x.Count()})
// .OrderByDescending(x => x.Count)
.ToListAsync();
var data = await dataTask;
它只能与聚合函数一起使用,例如像上面的 Count 函数。
在 SQL 中类似的语法也适用。
SELECT COUNT(R.Id), R.Source
FROM
[EntertainmentDb].[dbo].[Ratings] R
GROUP BY R.Source
但是,如果移除聚合函数,COUNT就不能用了,你会收到类似以下的消息:
列 'EntertainmentDb.dbo.Ratings.Id' 在选择列表中无效,因为它既不包含在聚合函数中,也不包含在GROUP BY子句中。
看起来我正在尝试向EF Core提出一个在TSQL中无法提问的问题。
AsEnumerable()
或类似方法)。如果您想加入/投票,请参见 GitHub 的问题/讨论 https://github.com/dotnet/efcore/issues/17068 。 - Ivan StoevGROUP BY
只支持选择关键字和聚合函数。LINQ的GroupBy
则允许选择关键字和相关项列表。这在SQL中没有相应的功能,这也是EF Core 3.0/3.1设计人员不愿意支持它的原因。EF6是一个不同的框架,因此它支持什么和不支持什么都是无关紧要的。我们能做的就是加入讨论/投票并努力说服EF Core团队改变他们目前的决定。 - Ivan Stoev