将 EF Core 查询从 2.2 转换为 3.0 - async await
Converting EF Core queries from 2.2 to 3.0 - async await
在 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 查询以使其使用异步/等待。我不知道如何在 单个 query/linq 表达式中包含 AsAsyncEnumerable()
。
(我知道我可以将它分成 'server' 部分和 'client-side' 部分,但我希望在单个 async[=32 中看到它=] 我之前在 EF Core 2.2 中使用的 linq 表达式。)
这个想法似乎是将 AsAsyncEnumerable()
与 System.Linq.Async 包结合起来,后者为 IAsyncEnumerable<T>
.
提供等效的 LINQ (IEnumerable<T>
) 扩展方法
所以根据想法,如果您安装(或包参考)那个包,在 .GroupBy
之前插入 .AsAsyncEnumerable()
,原始查询应该有效。
尽管 EF Core 3.0 DbSet<T>
class 存在一个烦人的问题。由于它实现了 IQueryable<T>
和 IAsyncEnumerable<T>
接口,并且其中 none 是 "better" (更接近)匹配,许多在 DbSet<T>
上使用标准 LINQ 运算符的查询将简单地在编译时使用 CS0121
(不明确的调用)中断,并且需要添加 .AsQueryable()
。使用 Include
/ ThenInclude
等 EF Core 特定扩展的查询将起作用,因为它们已经解析为 IQueryable<T>
.
正如一些人在评论中提到的那样,可以使用 (await [serverPart...].ToListAsync())[clientPart...]
来消除 System.Linq.Async
的需要和相关的编译时方法歧义,但它与使用 [=26 具有相同的缺点=] 而不是同步场景中的 AsEnumerable()
,方法是创建一个不必要的内存列表。
当然,最好的办法是通过找到等效但完全可翻译的 LINQ 构造来完全避免客户端评估。目前 GroupBy
很难,有时甚至是不可能的。即使有可能,它也需要通过删除 GroupBy
来重写以前的查询。例如,不是从 ArticleTranslation
开始查询并按 ArticleId
分组,而是从 Article
开始查询并使用 Translations
集合导航 属性 OrderByDescending()...FirstOrDefault()
支持。对每个失败的查询重复该过程。这样做的好处是,现在您的查询将首先执行服务器端。
我在 dotnet core 5.0 和 6.0 上遇到了同样的问题,我是这样解决的:
根据您的 dotnet 版本安装 nuget 包 System.Linq.Async https://www.nuget.org/packages/System.Linq.Async
然后在您的查询中您将能够添加 .ToAsyncEnumerable()
这是一个例子:
public async Task<IEnumerable<ProductTableView>> GetProduct(Pagination pagination, string identifier)
{
var ListProductsTable = await _context.ModelView
.FromSqlRaw("GetProductByIdentifier {0}", identifier)
.ToAsyncEnumerable()
.Select(a => new ProductTableView
{
ID = a.ID,
Guid = a.Guid,
Guid_Product_Category = a.Guid_Product_Category,
Guid_Currency = a.Guid_Currency,
NameProduct = a.NameProduct,
}).ToListAsync();
return ListProductsTable;
}
在 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 查询以使其使用异步/等待。我不知道如何在 单个 query/linq 表达式中包含 AsAsyncEnumerable()
。
(我知道我可以将它分成 'server' 部分和 'client-side' 部分,但我希望在单个 async[=32 中看到它=] 我之前在 EF Core 2.2 中使用的 linq 表达式。)
这个想法似乎是将 AsAsyncEnumerable()
与 System.Linq.Async 包结合起来,后者为 IAsyncEnumerable<T>
.
IEnumerable<T>
) 扩展方法
所以根据想法,如果您安装(或包参考)那个包,在 .GroupBy
之前插入 .AsAsyncEnumerable()
,原始查询应该有效。
尽管 EF Core 3.0 DbSet<T>
class 存在一个烦人的问题。由于它实现了 IQueryable<T>
和 IAsyncEnumerable<T>
接口,并且其中 none 是 "better" (更接近)匹配,许多在 DbSet<T>
上使用标准 LINQ 运算符的查询将简单地在编译时使用 CS0121
(不明确的调用)中断,并且需要添加 .AsQueryable()
。使用 Include
/ ThenInclude
等 EF Core 特定扩展的查询将起作用,因为它们已经解析为 IQueryable<T>
.
正如一些人在评论中提到的那样,可以使用 (await [serverPart...].ToListAsync())[clientPart...]
来消除 System.Linq.Async
的需要和相关的编译时方法歧义,但它与使用 [=26 具有相同的缺点=] 而不是同步场景中的 AsEnumerable()
,方法是创建一个不必要的内存列表。
当然,最好的办法是通过找到等效但完全可翻译的 LINQ 构造来完全避免客户端评估。目前 GroupBy
很难,有时甚至是不可能的。即使有可能,它也需要通过删除 GroupBy
来重写以前的查询。例如,不是从 ArticleTranslation
开始查询并按 ArticleId
分组,而是从 Article
开始查询并使用 Translations
集合导航 属性 OrderByDescending()...FirstOrDefault()
支持。对每个失败的查询重复该过程。这样做的好处是,现在您的查询将首先执行服务器端。
我在 dotnet core 5.0 和 6.0 上遇到了同样的问题,我是这样解决的:
根据您的 dotnet 版本安装 nuget 包 System.Linq.Async https://www.nuget.org/packages/System.Linq.Async
然后在您的查询中您将能够添加 .ToAsyncEnumerable()
这是一个例子:
public async Task<IEnumerable<ProductTableView>> GetProduct(Pagination pagination, string identifier)
{
var ListProductsTable = await _context.ModelView
.FromSqlRaw("GetProductByIdentifier {0}", identifier)
.ToAsyncEnumerable()
.Select(a => new ProductTableView
{
ID = a.ID,
Guid = a.Guid,
Guid_Product_Category = a.Guid_Product_Category,
Guid_Currency = a.Guid_Currency,
NameProduct = a.NameProduct,
}).ToListAsync();
return ListProductsTable;
}