LINQ where 子句中的 async/await 不起作用
Posted
技术标签:
【中文标题】LINQ where 子句中的 async/await 不起作用【英文标题】:async/await inside LINQ where clause not working 【发布时间】:2021-04-11 23:57:44 【问题描述】:我正在尝试在 LINQ 语句中进行异步数据库查询,但遇到了错误。下面的代码在没有 async/await 的情况下运行良好
var newEntities = _repositoryMapping.Mapper.Map<List<Entry>>(entries);
newEntities = newEntities.Where(async e => await !_context.Entries.AnyAsync(c => c.Id == e.Id)).ToList();
严重性代码描述项目文件行抑制状态 错误 CS4010 无法将异步 lambda 表达式转换为委托类型 '功能'。异步 lambda 表达式可能会返回 void,Task 或 Task,它们都不能转换为 '功能'
除了把它分解成一个 foreach 循环之外,我怎样才能使它与 async/await 一起工作?
【问题讨论】:
让这个异步 lamda 工作并多次访问数据库可能是错误的方法。让它与一个逻辑 sql 翻译一起工作将是更好的方法。您要做什么,只需检查给定实体的 ID 是否存在?您想在这里解决更大的问题吗? @Qbertsuit?为什么不简单地进行左连接(如果为 null)? @00110001 我只是想将 async/await 添加到一些现有代码中。目前它包含在一个没有意义的 Task.Run 中。我认为代码的原始作者只是想从数据库中已经存在的 newEntries 中删除任何项目 @PeterCsala 你能告诉我怎么做吗? 检查这些:1, 2 【参考方案1】:如果您关心性能,代码应该更智能。您只需要发送一个查询并检查数据库中已经存在的内容。
准备好的扩展,可以以通用方式做到这一点:
newEntities = (await newEntities.FilterExistentAsync(_context.Entries, e => e.Id)).ToList();
实现并不复杂
public static class QueryableExtensions
public static async Task<IEnumerable<T>> FilterExistentAsync<T, TProp>(this ICollection<T> items,
IQueryable<T> dbQuery, Expression<Func<T, TProp>> prop, CancellationToken cancellationToken = default)
var propGetter = prop.Compile();
var ids = items.Select(propGetter).ToList();
var parameter = prop.Parameters[0];
var predicate = Expression.Call(typeof(Enumerable), "Contains", new[] typeof(TProp) , Expression.Constant(ids), prop.Body);
var predicateLambda = Expression.Lambda(predicate, parameter);
var filtered = Expression.Call(typeof(Queryable), "Where", new[] typeof(T), dbQuery.Expression,
predicateLambda);
var selectExpr = Expression.Call(typeof(Queryable), "Select", new[] typeof(T), typeof(TProp), filtered, prop);
var selectQuery = dbQuery.Provider.CreateQuery<TProp>(selectExpr);
var existingIds = await selectQuery.ToListAsync(cancellationToken);
return items.Where(i => !existingIds.Contains(propGetter(i)));
【讨论】:
这对我来说看起来很复杂 :-) 但如果它有效的话真的很酷!我会试一试。非常感谢! 别担心,这是我之前写的最简单的扩展,它从第一次运行就开始工作了。【参考方案2】:对于Exception
,您可以为IEnumerable
添加扩展以支持async
public static class MyExtensions
public static async Task<IEnumerable<T>> Where<T>(this IEnumerable<T> source,
Func<T, Task<bool>> func)
var tasks = new List<Task<bool>>();
foreach (var element in source)
tasks.Add(func(element));
var results = await Task.WhenAll<bool>(tasks.ToArray());
var trueIndex = results.Select((x, index) => new x, index )
.Where(x => x.x)
.Select(x => x.index).ToList();
var filterSource = source.Where((x, index) => trueIndex.Contains(index));
return filterSource;
然后你可以使用下面的东西
var result = await users.Where(async x => await TestAsync(x));
完整代码在这里https://dotnetfiddle.net/lE2swz
【讨论】:
这似乎非常低效。如果source
包含大量项目,假设n
那么这将执行n
到数据库的往返次数以检查存在。
这是一个不错的解决方案,但我同意@PeterCsala。非常感谢您的意见!
这个解决方案通常很复杂O(n^2)
,但可以用O(n)
实现,只需使用source
和result
上的循环而不是trueIndex.Contains(index)
@PeterCsala 是的,你是对的,所以我说的只是例外以上是关于LINQ where 子句中的 async/await 不起作用的主要内容,如果未能解决你的问题,请参考以下文章
linq 到实体,where 子句中的 where ? (内凡)