C# use async lambda expression with return bool in Enumerable.All extension

Viewed 1260

I have the following async method

private async Task<bool> HasPolicy(AuthorizationFilterContext context, string policy)
{
    var authorized = await _authorization.AuthorizeAsync(context.HttpContext.User, policy);
    return authorized.Succeeded;
}

I would like to use it in Enumerable.All and Enumerable.Any extension

var result1 = IsAndPolicy
    ? policys.All(async x => await HasPolicy(context, x))
    : policys.Any(async x => await HasPolicy(context, x));

But the above code gets the following error

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

2 Answers

One approach would be to project to an IEnumerable<Task<bool>>

var results = await Task.WhenAll(policys.Select(x => HasPolicy(context, x)));

var result1 = IsAndPolicy 
   ? results.All(x => x)                
   : results.Any(x => x);

The trouble with this approach, is you are processing all the items potentially needlessly.

Michael Randall's answer produces the correct logical result but has to evaluate all policies, even though in both cases the evaluation can be short-circuited, by the first false result for All, or the first true result for Any.

This can be avoided by converting the tasks to an IAsyncenumerable<T> and using System.Linq.Async :

var resultTasks=policies.ToAsyncEnumerable()
                        .Select(async x => await HasPolicy(context, x));

var result1 = await (IsAndPolicy 
                       ? resultTasks.AllAsync(x => x)                
                       : resultTasks.AnyAsync(x => x));
Related