将 EF Core 查询从 2.2 转换为 3.0 - 异步等待

juF*_*uFo 5 c# entity-framework async-await entity-framework-core ef-core-3.0

在 EF Core 2.2 中,我有:

      var data = await _ArticleTranslationRepository.DbSet
        .Include(arttrans => arttrans.Article)
        .ThenInclude(art => art.Category)
        .Where(trans => trans.Article != null && trans.Article.Category != null && trans.Article.Category.Id == categoryId.Value)
        .GroupBy(trans => trans.ArticleId)
        .Select(g => new { ArticleId = g.Key, TransInPreferredLang = g.OrderByDescending(trans => trans.LanguageId == lang).ThenByDescending(trans => trans.LanguageId == defaultSiteLanguage).ThenBy(trans => trans.LanguageId).FirstOrDefault() })
        .Select(at => at.TransInPreferredLang)
        .OrderBy(at => at.Article.SortIndex)
        .ToListAsync();
Run Code Online (Sandbox Code Playgroud)

现在使用 EF Core 3.0 我不得不写:

      var data = _ArticleTranslationRepository.DbSet
  .Include(arttrans => arttrans.Article)
  .ThenInclude(art => art.Category)
  .Where(trans => trans.Article != null && trans.Article.Category != null && trans.Article.Category.Id == categoryId.Value)
    .AsEnumerable() // client side groupby is not supported (.net core 3.0 (18 nov. 2019)
  .GroupBy(trans => trans.ArticleId)
  .Select(g => new { ArticleId = g.Key, TransInPreferredLang = g.OrderByDescending(trans => trans.LanguageId == lang).ThenByDescending(trans => trans.LanguageId == defaultSiteLanguage).ThenBy(trans => trans.LanguageId).FirstOrDefault() })
  .Select(at => at.TransInPreferredLang)
  .OrderBy(at => at.Article.SortIndex)
  .ToList();

Run Code Online (Sandbox Code Playgroud)

我的 asp.net 核心 mvc actionmethod 是 async( public virtual async Task<ICollection<…>>…) 因为我使用 .AsEnumerable 来强制客户端评估我也不得不更改.ToListAsync().ToList()并删除await操作符。

查询正在运行,但会产生警告: This async method lacs 'await' operators and will run synchronously. Consider using the 'await operator ….

如何重写此 EF Core 3.0 查询以使其使用 async/await。我不知道如何将 包含AsAsyncEnumerable()单个查询/linq 表达式中。

(我知道我可以将它分成“服务器”部分和“客户端”部分,但我希望在单个异步linq 表达式中看到它,就像我之前在 EF Core 2.2 中所做的那样。)

Iva*_*oev 7

这个想法似乎AsAsyncEnumerable()System.Linq.Async包相结合,后者IEnumerable<T>IAsyncEnumerable<T>.

因此,根据想法,如果您安装(或包引用)该包,插入.AsAsyncEnumerable()before .GroupBy,有问题的原始查询应该可以工作。

尽管 EF Core 3.0DbSet<T>类存在一个烦人的问题。由于它同时实现IQueryable<T>IAsyncEnumerable<T>接口,并且它们都不是“更好”(更接近)匹配,因此许多使用标准 LINQ 运算符 on 的查询DbSet<T>将在编译时简单地中断CS0121(模棱两可的调用),并且需要添加.AsQueryable(). 使用 EF Core 特定扩展(如Include/ )的查询ThenInclude将起作用,因为它们已经解析为IQueryable<T>.

正如某些人在评论中提到的那样,可以使用(await [serverPart...].ToListAsync())[clientPart...]which 消除了System.Linq.Async相关的编译时方法歧义的需要,但它具有与通过创建不必要的内存列表ToList()而不是AsEnumerable()在同步场景中使用相同的缺点。


当然,最好的办法是通过寻找等效但完全可翻译的 LINQ 构造来完全避免客户端评估。目前GroupBy很难,有时甚至是不可能的。即使有可能,它也需要通过消除GroupBy. 例如,您可以从并使用支持的集合导航属性开始查询,而不是从 开始查询ArticleTranslation和分组依据。对每个失败的查询重复该过程。好处是,现在您的查询将按照最初的方式执行服务器端。ArticleIdArticleTranslationsOrderByDescending()...FirstOrDefault()