簡體   English   中英

EF Core 3.1 / EF Core 5.0 中的 GroupBy 不起作用,即使是最簡單的例子

[英]GroupBy in EF Core 3.1 / EF Core 5.0 not working, even for the simplest example

我正在將 EF6.x 項目更新到 EF Core 3.1。 決定回歸基礎並再次遵循如何從頭開始建立關系的示例。

根據微軟官方文檔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 框架從第一天起就一直在做的最簡單的組。這似乎是任何數據驅動應用程序的基本要求? (或任何具有少量數據的應用程序!)

更新: 在此處輸入圖片說明

添加屬性,例如相關博客的主鍵,沒有任何區別。

更新 2:

如果您按照這篇 JetBrains 文章,您可以這樣做:

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 不會,您會收到類似於以下內容的消息:

Column 'EntertainmentDb.dbo.Ratings.Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

所以看起來我正在嘗試向 EF Core 提出一個我無法在 TSQL 中提出的問題

當無法進行服務器端評估時,早期的 EF/EF 核心會自動轉換為客戶端查詢評估。

SQL 不支持按鍵分組而不選擇選擇,並且始終是客戶端操作。

在 EF 3.0+ 中,他們明確說明了哪個查詢應該在服務器上或客戶端上運行。 從技術上講,最好明確知道哪個查詢將在服務器上運行,什么將在客戶端運行,而不是由框架代表我們決定它。

您可以在此處閱讀更多相關信息: https : //docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/break-changes#linq-queries-are-不再由客戶評估

我認為這可能是 GroupBy 的不當使用,因為您實際上並未按新的或聚合數據進行分組,而是使用現有關系並選擇博客並包含帖子。

注意:未經測試的代碼

var blogs = ctx.Blogs.Include(x => x.Posts);
// Optional filters.
var blogsWithPosts = ctx.Blogs
    .Include(x => x.Posts)
    .Where(x => x.Posts.Any())  // Only blogs with posts
    .Where(x => x.Posts.Any(y => y.Title == "A Specific Title")) // Only blogs with posts that have the title "A Specific Title"

如果您只需要包含博客帖子的一個子集,您也可以這樣做。

var blogsAndMathingPosts = ctx.Blogs
    .Where(x => x.Posts.Any(y => y.Title == "A Specific Title")) // Only blogs that have at least one post with "A Specific Title"
    .Select(x => new Blog() {
        BlogId = x.BlogId,
        Url = x.Url,
        Posts = ctx.Posts.Where(y => y.BlogId == x.BlogId && y.Title == "A Specific Title").ToList()
    );

只需按照異常消息所說的去做! 您還需要將“var”更改為顯式。

我有這個並且有與您相同的異常消息:

var GroupByM2S =
            dbContext.CatL1s
           .GroupBy(x => x.UserId);   

我改成了這個。 經測試,工作正常。

IEnumerable<IGrouping<int, CatL1>> MsGrpByAsEnumerExplicit =              
            (dbContext.CatL1s).AsEnumerable()
           .GroupBy(x => x.UserId);

所以基本上像我一樣改變'var'。 *這里的int,IGrouping<int,...>是你的分組鍵Prop/Col的數據類型然后用pars包圍dbContext.EntityName,然后用.AsEnumerable().GroupBy(...)

IEnumerable<IGrouping<dataTypeOfGpByKey, EntityName>> GrpByIEnumAsEnumExplicit =
        ( //<--Open Par dbCtx.EntityName
    .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
            )).AsEnumerable() //<-- Put here
                    .GroupBy(
                    source: p => p.Inner,
                    keySelector: p => p.Outer)
                    ...

給任何有相同異常消息的人一個機會。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM