在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();
现在有了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();
我的asp.net核心mvc操作方法是异步的(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查询,使其使用异步/等待。我不知道如何在单个查询/ linq表达式中包含AsAsyncEnumerable()
。
(我知道我可以将其分为“服务器”部分和“客户端”部分,但我想像以前一样在单个 async linq表达式中看到它EF Core 2.2之前的版本。)
答案 0 :(得分:4)
这个想法似乎是将AsAsyncEnumerable()
与System.Linq.Async软件包组合在一起,该软件包为IEnumerable<T>
提供了等效的LINQ(IAsyncEnumerable<T>
)扩展方法。
因此,根据您的想法,如果您安装(或引用了软件包)该软件包,并在.AsAsyncEnumerable()
之前插入了.GroupBy
,则原始的相关查询应该可以正常工作。
尽管EF Core 3.0 DbSet<T>
类存在一个烦人的问题。由于它同时实现了IQueryable<T>
和IAsyncEnumerable<T>
接口,并且它们都不是“更好的”(更紧密的)匹配,因此许多在DbSet<T>
上使用标准LINQ运算符的查询都将在编译时使用{ {1}}(歧义通话),并且需要添加CS0121
。使用.AsQueryable()
/ Include
之类的EF Core特定扩展的查询将起作用,因为它们已经解析为ThenInclude
。
正如评论中的某些人所提到的,可以使用IQueryable<T>
来消除对(await [serverPart...].ToListAsync())[clientPart...]
的需求以及相关的编译时方法的歧义性,但是它与使用System.Linq.Async
的缺点相同通过创建不必要的内存中列表,而不是在同步方案中使用ToList()
。
当然,最好的办法是通过找到等效但完全可翻译的LINQ构造来完全避免客户评估。当前使用AsEnumerable()
的代码很难,有时甚至是不可能的。甚至有可能,它需要通过消除GroupBy
来重写先前的查询。例如,您可以从GroupBy
开始查询,并将ArticleTranslation
集合导航属性与{{1}一起使用,而不是从ArticleId
开始查询并按Article
进行分组。 }。对每个失败的查询重复该过程。这样做的好处是现在您的查询将首先在服务器端执行。