Why does Task.WhenAll return void? - c#

This is the code from the image above:
if (claims != null && claims.Any())
{
// firstly, why doesn't this work?
// var _claimResults = from claim in claims select UserManager.AddClaimAsync(user.Id, claim);
// but this does...
List<Task> _claimResults = new List<Task>();
foreach (var claim in claims)
{
_claimResults.Add(UserManager.AddClaimAsync(user.Id, claim));
}
// secondly, why does Task.WhenAll return void when it clearly says it returns Task?
Task claimsResult = await Task.WhenAll(_claimResults);
}
Why doesn't the LINQ expression work, yet the foreach does. The LINQ expression gives me a "underlying provider failed to open" exception on execution.
Why does Task.WhenAll() return void when it says it's return type is Task?
Edit: claims is a List<Claim> which I think is List<System.Security.Claim>.

WhenAll returns a Task, but then you're awaiting that task. Awaiting a plain Task (rather than a Task<T>) gives no result. So you either want:
Task claimsResult = Task.WhenAll(_claimResults);
or
await Task.WhenAll(_claimResults);
My suspicion is that the LINQ problem is because your foreach approach materializes the query immediately - the LINQ equivalent would be:
var _claimsResults = claims.Select(claim => UserManager.AddClaimAsync(user.Id, claim))
.ToList();
... where the ToList() method materializes the results immediately. (I've used the method call syntax rather than query expression syntax because query expressions are pretty pointless for trivial queries like this.)

Not sure about #1, but for #2 its because you called await on it
it should be
Task claimsResult = Task.WhenAll(_claimResults);
await claimsResult;
or omit the variable entirely if you don't need to delay the await somewhere else

Related

Where clause in LINQ calling an async method

I have a method in a class defined as....
public static async Task<int> GetDCountAsync(int dId)
I'm trying to call this method within a LINQ Where clause....
var excessD= dToConsider
.Where(async x => await myService.GetDCountAsync(x.Id) >= x.Threshold)
.Select(x => x.Id)
.ToArray();
FYI: Threshold is an int.
I'm getting an error for async....
The return type of an 'async' anonymous function must be a 'void', 'Task', 'Task', ......
The return type of GetDCountAsync is async Task. Where am I going wrong? Thanks in advance!
You could use the functionality already available in the System.Linq.Async package:
int[] excessD = await dToConsider
.ToAsyncEnumerable()
.WhereAwait(async x => await myService.GetDCountAsync(x.Id) >= x.Threshold)
.Select(x => x.Id)
.ToArrayAsync();
The signature of the WhereAwait operator:
// 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);
The asynchronous method myService.GetDCountAsync will be invoked and awaited for one element at a time, not for all of them concurrently.
So the full error message you are getting will be :
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>'.
What it's actually telling you is that a .Where() LINQ call is expecting to take an int, and return a boolean. But because you are trying to do an async call, it's instead trying to return Task.
Or in other words, the compiler is saying "I see you are using an async method here, I know that async methods can only return void, Task or Task, and I also know that this Where method expects a bool back, so I know right now this is not gonna work"
Part of the problem is that we don't actually know what type dToConsider is, and in some cases, it's going to look to do deferred execution. In any case, because you are doing custom code logic for the WHERE statement, let's assume that this is not EntityFramework or something that needs to be deferred and say it's just a list. In that case, simple using a typical ForEach loop to filter your list down.
var filteredItems = new List<T>();
foreach(var item in dToConsider)
{
if(await myService.GetDCountAsync(x.Id) >= x.Threshold)
filteredItems.Add(item);
}
I'm sure someone may answer with a nice extension method for you using Task.WhenAll etc, but the this is the simplest way to get what you need done.

How can I assign the value of a property, within each task of a Task.WhenAll?

How can I set a Task.WhenAll() result to a value within a Task.WhenAll() routine? For example, the following code will get all authorized users for all domain/group combos in appRoleMaps:
var results = await Task.WhenAll(appRoleMaps.Select(
x => GetAuthorizedUsers(x.DomainName, x.ADGroupName)));
But what if I want to set the authorized users results of each iteration as the iteration item Authorized property value? For example, something like the following code (although the following code does not work):
var results = await Task.WhenAll(appRoleMaps.Select(
x => x.AuthorizedUsers = GetAuthorizedUsers(x.DomainName, x.ADGroupName)));
Is there a streamlined way to do this? Reason being, the GetAuthorizedUsers result set does not include domain/group info, so I can't just do a simple foreach/where at the end to easily join by this info.
You could create an async lambda to pass into your Select. This would cause each result to be assigned to the AuthorizedUsers property on the associated instance. The outer Task.WhenAll is only required to know when all elements have been processed.
await Task.WhenAll(appRoleMaps.Select(async x =>
x.AuthorizedUsers = await GetAuthorizedUsers(x.DomainName, x.ADGroupName)));
Save the tasks and query them after waiting:
var tasks = appRoleMaps.Select(x => GetAuthorizedUsers(x.DomainName, x.ADGroupName)).ToList();
await Task.WhenAll(tasks);
var results = tasks.Select(t => t.Result).ToList();
This is cleaner than relying on side effects. Squirreling the value away into some property and later extracting it obfuscates the meaning of the code and is more work to code.

Is there a way to combine LINQ and async

Basically I have a procedure like
var results = await Task.WhenAll(
from input in inputs
select Task.Run(async () => await InnerMethodAsync(input))
);
.
.
.
private static async Task<Output> InnerMethodAsync(Input input)
{
var x = await Foo(input);
var y = await Bar(x);
var z = await Baz(y);
return z;
}
and I'm wondering whether there's a fancy way to combine this into a single LINQ query that's like an "async stream" (best way I can describe it).
When you use LINQ, there are generally two parts to it: creation and iteration.
Creation:
var query = list.Select( a => a.Name);
These calls are always synchronous. But this code doesn't do much more than create an object that exposes an IEnumerable. The actual work isn't done till later, due to a pattern called deferred execution.
Iteration:
var results = query.ToList();
This code takes the enumerable and gets the value of each item, which typically will involve the invocation of your callback delegates (in this case, a => a.Name ). This is the part that is potentially expensive, and could benefit from asychronousness, e.g. if your callback is something like async a => await httpClient.GetByteArrayAsync(a).
So it's the iteration part that we're interested in, if we want to make it async.
The issue here is that ToList() (and most of the other methods that force iteration, like Any() or Last()) are not asynchronous methods, so your callback delegate will be invoked synchronously, and you’ll end up with a list of tasks instead of the data you want.
We can get around that with a piece of code like this:
public static class ExtensionMethods
{
static public async Task<List<T>> ToListAsync<T>(this IEnumerable<Task<T>> This)
{
var tasks = This.ToList(); //Force LINQ to iterate and create all the tasks. Tasks always start when created.
var results = new List<T>(); //Create a list to hold the results (not the tasks)
foreach (var item in tasks)
{
results.Add(await item); //Await the result for each task and add to results list
}
return results;
}
}
With this extension method, we can rewrite your code:
var results = await inputs.Select( async i => await InnerMethodAsync(i) ).ToListAsync();
^That should give you the async behavior you're looking for, and avoids creating thread pool tasks, as your example does.
Note: If you are using LINQ-to-entities, the expensive part (the data retrieval) isn't exposed to you. For LINQ-to-entities, you'd want to use the ToListAsync() that comes with the EF framework instead.
Try it out and see the timings in my demo on DotNetFiddle.
A rather obvious answer, but you have just used LINQ and async together - you're using LINQ's select to project, and start, a bunch of async Tasks, and then await on the results, which provides an asynchronous parallelism pattern.
Although you've likely just provided a sample, there are a couple of things to note in your code (I've switched to Lambda syntax, but the same principals apply)
Since there's basically zero CPU bound work on each Task before the first await (i.e. no work done before var x = await Foo(input);), there's no real reason to use Task.Run here.
And since there's no work to be done in the lambda after call to InnerMethodAsync, you don't need to wrap the InnerMethodAsync calls in an async lambda (but be wary of IDisposable)
i.e. You can just select the Task returned from InnerMethodAsync and await these with Task.WhenAll.
var tasks = inputs
.Select(input => InnerMethodAsync(input)) // or just .Select(InnerMethodAsync);
var results = await Task.WhenAll(tasks);
More complex patterns are possible with asynchronony and Linq, but rather than reinventing the wheel, you should have a look at Reactive Extensions, and the TPL Data Flow Library, which have many building blocks for complex flows.
Try using Microsoft's Reactive Framework. Then you can do this:
IObservable<Output[]> query =
from input in inputs.ToObservable()
from x in Observable.FromAsync(() => Foo(input))
from y in Observable.FromAsync(() => Bar(x))
from z in Observable.FromAsync(() => Baz(y))
select z;
Output[] results = await query.ToArray();
Simple.
Just NuGet "System.Reactive" and add using System.Reactive.Linq; to your code.

Async Await in Lambda expression where clause

I would want to call an async method inside lambda expression. Please help me doing the below
eg -
return xyz.Where(async x=> await AsyncMethodCall(x.val));
And the Async method looks like
public async Task<bool> AsyncMethodCall(Data d){...}
When I do the above, I get the following error
Error CS4010 Cannot convert async lambda expression to delegate type
'Func<Data, bool>'. An async lambda expression may return void, Task
or Task<T>, none of which are convertible to 'Func<Data, bool>'.
Thanks in advance for the help
Asynchronous sequences are tricky because you really have to think about what specifically you want the code to do.
For example, you could want to execute the AsyncMethodCall calls sequentially, and then return all the results:
var result = new List<T>();
foreach (var d in xyz)
if (await AsyncMethodCall(d.val))
result.Add(d);
return result;
Or, you could execute all the AsyncMethodCall calls concurrently, and then collect and return the results (again, all at once):
var tasks = xyz.Select(async d => new { d, filter = await AsyncMethodCall(d.val) });
var results = await Task.WhenAll(tasks);
return results.Where(x => x.filter).Select(x => x.d);
Or, you could execute all the AsyncMethodCall calls sequentially, and produce the results one at a time. This approach is incompatible with IEnumerable<T> (assuming you want to keep the call asynchronous). If you want to produce a sequence where AsyncMethodCall is asynchronously invoked during sequence enumeration, then you would need to change to IAsyncEnumerable<T>. If you want to produce a sequence that is started by the consumer and then produces results on its own, you would need to change to IObservable<T>.
Or, you could execute all the AsyncMethodCall calls concurrently, and produce the results one at a time. This is also incompatible with IEnumerable<T>; you would need to change to IObservable<T>. And you would also need to decide whether to maintain the original ordering, or to produce them in order of AsyncMethodCall completing.

Counting Non-Faulted Tasks causes re-execution of each task

I am saving a bunch of items to my database using async saves
var tasks = items.Select(item =>
{
var clone = item.MakeCopy();
clone.Id = Guid.NewGuid();
return dbAccess.SaveAsync(clone);
});
await Task.WhenAll(tasks);
I need to verify how many times SaveAsync was successful (It throws and exception if something goes wrong). I am using IsFaulted flag to examine the tasks:
var successCount = tasks.Count(t => !t.IsFaulted);
Collection items consists of 3 elements so SaveAsync should have been called three times but it is executed 6 times. Upon closer examination I noticed that counting non-faulted tasks with c.Count(...) causes each of the task to re-run.
I suspect it has something to do with deferred LINQ execution but I am not sure why exactly and how to fix this.
Any suggestion why I observe this behavior and what would be the optimal pattern to avoid this artifact?
It happens because of multiple enumeration of your Select query.
In order to fix it, force enumeration by calling ToList() method. Then it will work correctly.
var tasks = items.Select(item =>
{
var clone = item.MakeCopy();
clone.Id = Guid.NewGuid();
return dbAccess.SaveAsync(clone);
})
.ToList();
Also you may take a look at these more detailed answers:
https://stackoverflow.com/a/8240935/3872935
https://stackoverflow.com/a/20129161/3872935.

Categories