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.
Related
I've been refactoring a common pattern in my project and found it's not as simple as using a LINQ Select to an async function.
For context, here is how it is done currently.
async Task<ICollection<Group>> ExecuteQueryGroupsForDomain(DomainInfo domain, int batchSize, CancellationToken ct)
{
try
{
return await BlahBlahActuallyGoGetGroupsForDomainHere(domain, batchSize, ct);
}
catch (Exception e)
{
return null;
}
}
var executeQueries = new List<Func<CancellationToken, Task<ICollection<Group>>>>();
domains.ForEach(domain =>
executeQueries.Add(async (ct) =>
await ExecuteQueryGroupsForDomain(domain, 123, ct)));
Now if I try to replace the ForEach loop section using LINQ:
var executeQueries = domains.Select(domain =>
async (ct) => await ExecuteQueryGroupsForDomain(domain, 123, ct));
It complains Type arguments cannot be inferred by the usage which leads me to believe I'm not returning anything from the Select, but I clearly am returning the Func I want.
Is there a better way of creating a list of Func's, ideally avoiding explicit casts? Also is there any explanation why the compiler is unable to infer the type when the Select'd async method is clearly telling it what the type should be?
To be clear, I do need to pass the CancellationToken to the Func's because it is a different token than the external one (specifically, it is a linked token tying the external one to another internal one).
The problem is in the returns of the select, for the compiler is not clear what the type of the return is. So, you need to explicitly the type of the return, here are 2 ways:
executeQueries = domains.Select(domain =>
new Func<CancellationToken, Task<ICollection<Group>>>(token =>
this.ExecuteQueryGroupsForDomain(domain, 123, token))).ToList();
executeQueries = domains
.Select<DomainInfo, Func<CancellationToken, Task<ICollection<Group>>>>(domain =>
ct => this.ExecuteQueryGroupsForDomain(domain, 123, ct)).ToList();
======================================================================
EDIT 1:
The compiler can't infer the type from a lambda expression because a lambda is just a shorthand for an anonymous method, not a type. So, you need to be explicitly and indicate the return type of the method, if the return is a base Delegate or other delegate type, like Action, Func, etc. Review this other answer, where explain the error compiler based on the C# 4 spec.
If you need transform your original code in something more readable, I don't think there is another way more readable. Here are other ways the code can be written:
foreach (var domain in domains) {
executeQueries.Add(token => this.ExecuteQueryGroupsForDomain(domain, 123, token));
}
executeQueries.AddRange(domains
.Select(domain => (Func<CancellationToken, Task<ICollection<Group>>>) (token =>
this.ExecuteQueryGroupsForDomain(domain, 123, token))));
executeQueries =
(from domain in domains
select new Func<CancellationToken, Task<ICollection<Group>>>(token =>
this.ExecuteQueryGroupsForDomain(domain, 123, token))).ToList()
Do you really need the Func's?
You can use the following if the actual CancellationToken is already present.
// create and start a Task for each domain
var executeQueryTasks = domains.Select(domain => ExecuteQueryGroupsForDomain(domain, 123, ct));
// wait until all tasks are finished and get the result in an array
var executedQueries = await Task.WhenAll(executeQueryTasks);
You may gain some readability by using an extension method like the one below. It takes the same arguments with the LINQ Select method, but returns task-factories instead of materialized tasks.
public static IEnumerable<Func<CancellationToken, Task<TResult>>> SelectTaskFactory
<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, CancellationToken, Task<TResult>> selector)
{
return source.Select(item =>
{
return new Func<CancellationToken, Task<TResult>>(ct => selector(item, ct));
});
}
Usage example:
var executeQueries = domains.SelectTaskFactory(async (domain, ct) =>
{
return await ExecuteQueryGroupsForDomain(domain, 123, ct);
}).ToList();
The type of the executeQueries variable is List<Func<CancellationToken, Task<ICollection<Group>>>>.
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.
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.
I'm currently implementing the repository pattern for my ASP.VNext application. I would like the methods to be asynchronous and filterable. So I have devised the following interface method:
Task<TEntity> GetOneAsync(Func<TEntity,bool> predicate);
and would like to implement it like this (with a private DbContext instance ctx):
public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate)
{
// compiler error
return await ctx.MyEntities.Where(predicate).FirstOrDefaultAsync();
}
However I can only use FirstOrDefaultAsync() when hardcoding the predicate like this:
return await ctx.MyEntites.Where(e => e.Id == 1).FirstOrDefaultAsync();
When passing the predicate i only get the FirstOrDefault() without the async option, so in order to make my method asynchronous I have to write
public async Task<MyEntity> GetOneAsync(Func<MyEntity,bool> predicate)
{
//save to a local variable to prevent calling a disposed DbContext
var entities = await Task.Run(() => ctx.Contracts.Where(predicate).FirstOrDefault());
return entities;
}
I have two questions regarding this:
Why is it not possible to access the FirstOrDefaultAsync() method when passing a predicate?
Does my solution using await Task.Run(synchronousMethod) achieve the same behavior as a call to FirstOrDefaultAsync() would?
FirstOrDefaultAsync is defined as an extension method for IQueryable<T>.
ctx.MyEntities.Where(e => e.Id == 1) returns IQueryable<MyEntity>.
ctx.MyEntities.Where(predicate) returns IEnumerable<MyEntity>, because you're calling the Enumerable.Where extension method, not the Queryable.Where one.
To make it work, change predicate from Func<MyEntity, bool> to Expression<Func<MyEntity, bool>>. This means predicate is no longer just a function that gives the result you want, but a description of that function, that Entity Framework can then translate to SQL.
And no, using Func<MyEntity, bool> within a task would not have the same behaviour. That would load rows from the db server without any filtering, and evaluate each and every one at the db client until a match is found. That would add a lot of overhead.
Initial Statement
I would like to call asynchronously an operation to which I pass a delegate as a parameter (details below). The compiler gives me an error. Could someone point me in the right direction?
Async function:
private async Task<Route> FindRouteAsync(Destination Destination,
Func<Destination, bool> predicate){...
List<Destination> _Destinations = __Route.Destinations.Where(predicate).ToList();
...}
Calling code:
private async Task<List<Route>> FindRoutesAsync(Destination[] Destinations){...
Route _DestinationRoute = await FindRouteAsync(__Destination,
d => d == __Destination);
...}
The compilation error:
The 'await' operator can only be used within an async lambda expression. Consider marking this lambda expression with the 'async' modifier.
Updated Statement 1
#svick, #Stephen Cleary: Thank you guys! You were right, the problem was just as you expected in an outer loop:
What I had was (simplified):
Parallel.ForEach<Destination>(Destinations, __Destination =>
{
Route _DestinationRoute = await FindRouteAsync(__Destination,
d => d == __Destination);
}
Because of this lambda expression __Destination => {...} the code wouldn't compile. I turned it into __Destination => async {...} and now it works.
Now it looks like this:
Parallel.ForEach<Destination>(Destinations, async __Destination =>
{
try
{
// First, try to find an exact match
Route _DestinationRoute = await FindRouteAsync(__Destination, d => d == __Destination);
if (_DestinationRoute.ConnectionId != 0)
{ _DestinationRoutes.Enqueue(_DestinationRoute); }
...
}
catch...
});
So I was just looking at the wrong lambda expression in my code. The other one was causing all the fuss.
Thank you again! Lesson learned: "don't jump so fast to assumptions in the future".
PS: it's my first time here and maybe you can help me with giving credit where is due. I think the contributions from svick, Stephen Cleary and (in perspective) Javalsu were helpful. What do I do now? In all fairness, svick's comment led me to the code analysis that showed me the error in the end.
Updated Statement 2
It seems the whole construct of Parallel.ForEach with await inside the loop was flawed and the solution had poor chances of success. More details can be found here: Nesting await in Parallel foreach.
Try changing it to the following
private async Task<List<Route>> FindRoutesAsync(Destination[] Destinations){...
Route _DestinationRoute = await FindRouteAsync(__Destination,
async d => d == __Destination);
...}
I'm not near my desk so I can't test it.