【问题标题】:async/await inside LINQ where clause not workingLINQ where 子句中的 async/await 不起作用
【发布时间】:2026-01-03 17:40:02
【问题描述】:

我正在尝试在 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

标签: c# linq async-await


【解决方案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)实现,只需使用sourceresult上的循环而不是trueIndex.Contains(index)
  • @PeterCsala 是的,你是对的,所以我说的只是例外