关于c#:在嵌套LINQ-Select中使用Async / Await而不需要使用Task.WhenAll

Using Async/Await inside nested LINQ-Select without having to use Task.WhenAll

我试图在另一个选择语句内的一个选择语句内等待异步操作。

1
2
3
4
5
6
7
8
9
var result = someList
    .Select(table => new Table
    {
        Columns = table.Select(async column => new Column
        {
            Constraints = await GetColumnConstraints()
        })
    })
    .ToList();

这里的"问题"是嵌套的select语句返回任务列表。 通常,我们将使用Task.WhenAll()等待所有任务。 因此,最简单的方法是将Columns的类型从List更改为List>,然后使用SelectMany()来获取每个Task并等待它们。 但是我不能更改列的数据类型。

我怎样才能做到这一点? 我找不到不涉及Task.WhenAll()的任何解决方案


您不需要更改Column的类型,只需要在表的初始化程序之外对await的结果进行更改。这样,您就可以在columnTasksawait中收集所有结果,然后创建新表。

1
2
3
4
5
6
7
8
9
10
11
12
var result = someList.Select(async table =>
{
    var columnTasks = table.Select(async column => new Column()
    {
        Constraints = await GetColumnConstraints()
    });
    var columns = await Task.WhenAll(columnTasks);
    return new Table()
    {
        Columns = columns
    };
});

请注意,尽管async沿链条向上移动,所以reuslt现在是IEnumerable>,您必须await Task.WhenAll才能获得Table的最终集合

1
var tables = await Task.WhenAll(result);

显然,SomelistTables的序列,其中每个Table都具有Columns的序列。

在我看来,这些表的列不影响GetColumnConstraints()。为什么每列调用一次此函数?如果只调用一次它会不会更有效率?

1
2
3
4
5
6
7
8
9
10
var columnConstraints = await GetColumnConstraintsAsync();
var result = tables.Select(table => new Table
{
    Columns = table.Select(column => new Column
    {
        Constraints = columnConstraints,
        ...
    })
})
.ToList();

可能是因为您简化了问题,而被枚举的表和列确实会影响所获取的约束,例如,因为它们被用作输入变量。也许,如果您第二次调用该函数,则会得到不同的约束?

如果确实需要获取每个表的每个列的约束,则在创建表之前首先等待获取约束:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var result = tables.Select(table =>
{
    var columnTasks = table.Select(column => GetColumnContraintsAsync(...)).ToArray();
    // all tasks are running now, wait until all are completed:
    await Task.WhenAll(columnTasks);
    // fetch the result from every task:
    var columnTaskResults = columnTasks.Select(columnTask => columnTask.Result).ToList();

    // create a Table with these results and return it:
    return new Table
    {
        Columns = columnTaskResults,
    };
})
.ToList();