将 EF Core 查询从 2.2 转换为 3.0 - 异步等待 [英] Converting EF Core queries from 2.2 to 3.0 - async await

查看:24
本文介绍了将 EF Core 查询从 2.2 转换为 3.0 - 异步等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 EF Core 2.2 中,我有:

In EF Core 2.2 I had:

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

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

Now with EF Core 3.0 I had to write:

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

我的 asp.net core mvc actionmethod 是 async (public virtual async Task>…)因为我使用 .AsEnumerable 来强制客户端评估,所以我还必须将 .ToListAsync() 更改为 .ToList() 并删除 await 运算符.

My asp.net core mvc actionmethod is async (public virtual async Task<ICollection<…>>…) Because I used .AsEnumerable to force client side evaluation I also had to change .ToListAsync() to .ToList() and remove the await operator.

查询正在运行,但产生警告:这个异步方法缺少await"操作符,并且会同步运行.考虑使用 'await 运算符 ....

The query is working but produces a warning: This async method lacs 'await' operators and will run synchronously. Consider using the 'await operator ….

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

How can this EF Core 3.0 query be rewritten so that it uses async / await. I can't figure out how to include the AsAsyncEnumerable() in a single query/linq expression.

(我知道我可以将它分成服务器"部分和客户端"部分,但我希望在单个 async linq 表达式中看到它在 EF Core 2.2 之前.)

(I know that I can split it up in a 'server' part and a 'client-side' part, but I would like to see it in a single async linq expression as I had before in EF Core 2.2.)

推荐答案

这个想法似乎是将 AsAsyncEnumerable()System.Linq.Async 包,它为 IAsyncEnumerable 提供等效的 LINQ (IEnumerable) 扩展方法;.

The idea seems to be combining the AsAsyncEnumerable() with System.Linq.Async package which provides equivalent LINQ (IEnumerable<T>) extension methods for IAsyncEnumerable<T>.

因此,如果您安装(或包引用)该包,在 .GroupBy 之前插入 .AsAsyncEnumerable(),那么原始查询应该可以工作.

So by idea if you install (or package reference) that package, inserting .AsAsyncEnumerable() before .GroupBy, the original query in question should work.

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

There is an annoying issue though with EF Core 3.0 DbSet<T> class. Since it implements both IQueryable<T> and IAsyncEnumerable<T> interfaces, and none of them is "better" (closer) match, many queries using standard LINQ operators on DbSet<T> will simply break at compile time with CS0121 (ambiguous call) and will require adding .AsQueryable(). Queries which use EF Core specific extensions like Include / ThenInclude will work because they already resolve to IQueryable<T>.

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

As some people mentioned in the comments, it's possible to use (await [serverPart...].ToListAsync())[clientPart...] which eliminates the need of System.Linq.Async and associated compile time method ambiguity, but it has the same drawback as using ToList() instead of AsEnumerable() in the synchronous scenario by creating an unnecessary in-memory list.

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

Of course the best would be to avoid client evaluation at all by finding equivalent, but fully translatable LINQ construct. Which currently with GroupBy is hard, and sometimes even impossible. And even it is possible, it requires rewriting the previous query by eliminating the GroupBy. For instance, instead of starting the query from ArticleTranslation and grouping by ArticleId, you might start the query from Article and use the Translations collection navigation property with OrderByDescending()...FirstOrDefault() which is supported. Repeat the procedure for each failing query. The benefit will be that now your queries will execute server side as they should in the first place.

这篇关于将 EF Core 查询从 2.2 转换为 3.0 - 异步等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆