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 中所做的那样。)
这个想法似乎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
和分组依据。对每个失败的查询重复该过程。好处是,现在您的查询将按照最初的方式执行服务器端。ArticleId
Article
Translations
OrderByDescending()...FirstOrDefault()