LINQ 中的 Where 子句调用异步方法

Where clause in LINQ calling an async method

我在 class 中有一个方法定义为....

public static async Task<int> GetDCountAsync(int dId)

我正在尝试在 LINQ Where 子句中调用此方法....

var excessD= dToConsider
    .Where(async x => await myService.GetDCountAsync(x.Id) >= x.Threshold)
    .Select(x => x.Id)
    .ToArray();

仅供参考:阈值是一个整数。

我收到异步错误....

The return type of an 'async' anonymous function must be a 'void', 'Task', 'Task', ......

GetDCountAsync 的 return 类型是异步任务。我哪里错了?提前致谢!

因此您收到的完整错误消息将是:

Cannot convert async lambda expression to delegate type 'Func<int, bool>'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Func<int, bool>'.

它实际上告诉您的是 .Where() LINQ 调用需要一个 int,return 一个布尔值。但是因为您正在尝试进行异步调用,所以它会尝试 return Task.

或者换句话说,编译器说“我看到你在这里使用异步方法,我知道异步方法可以 only return void, Task或 Task,我也 知道 这个 Where 方法需要一个 bool 返回,所以我现在知道这是行不通的

部分问题是我们实际上并不知道 dToConsider 是什么类型,在某些情况下,它会寻找延迟执行。在任何情况下,因为您正在为 WHERE 语句执行自定义代码逻辑,所以我们假设这不是 EntityFramework 或需要延迟的东西,并说它只是一个列表。在这种情况下,只需使用典型的 ForEach 循环来过滤您的列表即可。

var filteredItems = new List<T>();
foreach(var item in dToConsider)
{
    if(await myService.GetDCountAsync(x.Id) >= x.Threshold)
        filteredItems.Add(item);
}

我相信有人可能会使用 Task.WhenAll 等为您提供一个很好的扩展方法,但这是完成您需要完成的工作的最简单方法。

您可以使用 System.Linq.Async 包中已有的功能:

int[] excessD = await dToConsider
    .ToAsyncEnumerable()
    .WhereAwait(async x => await myService.GetDCountAsync(x.Id) >= x.Threshold)
    .Select(x => x.Id)
    .ToArrayAsync();

WhereAwait操作员的签名:

// Filters the elements of an async-enumerable sequence based on
// an asynchronous predicate.
public static IAsyncEnumerable<TSource> WhereAwait<TSource>(
    this IAsyncEnumerable<TSource> source,
    Func<TSource, ValueTask<bool>> predicate);

异步方法 myService.GetDCountAsync 将一次为一个元素调用和 awaited,而不是同时为所有元素调用和编辑。