When using an anonymous method in a LINQ Select statement does the anonymous method have to return a value?
When I do the following I get no errors:
await Task.WhenAll(list.Select(a => doSomething(a)));
But when I do this I get an error that says type arguments cannot be inferred from the usage:
await Task.WhenAll(list.Select(a => {
doSomething(a);
Log("Log Something");
UpdateUI();
}));
Why does the first work and the second doesn't?
Here is the doSomething method:
private async Task doSomething(string a)
{
HttpClient client = new HttpClient;
// Do stuff
string source = await client.PostAsync(a, content);
// Extract data from source and store in text file.
}
When using an anonymous method in a LINQ Select statement does the anonymous method have to return a value?
Yes. The signature of the Select method is:
public IEnumerable<TResult> Select<TSource, TResult>(
IEnumerable<TSource> source,
Func<TSource, TResult> selector)
so the selector must return a value.
With your first code snippet the return statement is implicit. doSomething returns a value, and that value is what each item is projected to.
When you use a statement lambda, instead of an expression lambda, there is no implicit return statement. Since your second code block is not returning anything, it doesn't match what Select expects.
Now, as for your actual problem. What you want to do is project each task into a task that does something, then writes to the log when it's done and updates the UI. You can use an async lambda to do this. In an async lambda when there are no return statement it will still be returning a Task (just without a Result) instead of void. And that's exactly what you want to do, project each task into another task.
await Task.WhenAll(list.Select(async a => {
await doSomething(a);
Log("Log Something");
UpdateUI();
}));
The first one is a simple expression, so the type of that expression is used as the return type of the lambda.
From MSDN:
A lambda expression with an expression on the right side of the => operator is called an expression lambda. Expression lambdas are used extensively in the construction of Expression Trees (C# and Visual Basic). An expression lambda returns the result of the expression
(Emphasis mine)
However, what you have is a statement lambda, which means that in order to return a value, you must have a return statement in the lambda body.
Yes, the function you pass to Select() has to return a value, because the purpose of Select is to change one set of values into another set of values. How about this:
Define this method:
private async Task DoSomethingLogAndUpdate(string a)
{
await doSomething(a);
Log("Log Something");
UpdateUI();
}
Then do:
await Task.WhenAll(list.Select(a => DoSomethingLogAndUpdate(a)));
Or to do this without defining a separate method:
await Task.WhenAll(list.Select(async a => {
await doSomething(a);
Log("Log Something");
UpdateUI();
}));
Related
I've minimised my issue to this:
I have a DbContext with a DbSet<PageBlock> and I get an IQueryable with a simple LINQ "Where" like so:
IQueryable<PageBlock> result = _context.PageBlocks.Where(x => x.PageId == 2);
But if I wrap this line in a method and pass a Func<PageBlock, bool> to the method for the where clause like this:
private IQueryable<PageBlock> GetQueryable(Func<PageBlock, bool> belongsToPage)
{
IQueryable<PageBlock> result = _context.PageBlocks.Where(belongsToPage);
...
}
and call it like this:
var result = GetQueryable(x => x.PageId == 2)
then the .Where now returns an IEnumerable and I get a compile error!? The IDE says I can put an explicit cast on like this:
IQueryable<PageBlock> result =
IQueryable<PageBlock>)_context.PageBlocks.Where(belongsToPage);
but that gives a runtime error
Unable to cast object of type 'WhereEnumerableIterator``1[DAL.Models.PageBlock]' to type 'System.Linq.IQueryable``1[DAL.Models.PageBlock]'.
Why does using the same filter indirectly via the Func param cause my query to return IEnumerable instead of IQueryable ?!
If you want to stay in the IQueryable world then everything in your query has to be executed via EntityFramework in your database. However what the C# compiler sees is that you want to use and call an arbitrary function. The only way it make that work is to enumerate the query so far and call that function on every returned value. It doesn't matter that your specific function could be executed by the database.
If you want to call your method like this var result = GetQueryable(x => x.PageId == 2) you should mimic the signature of Queriable.Where and accept an Expression<Func<TSource,bool>> predicate as your argument.
private IQueryable<PageBlock> GetQueryable(Expression<Func<PageBlock, bool>> belongsToPage)
{
IQueryable<PageBlock> result = _context.PageBlocks.Where(belongsToPage);
...
}
That way the query provider can examin the expression and see if it can be passed to the database
I would like to create a lambda expression and invoke it immediately and I would like to avoid creating a delegate; a trivial example1:
int i = (() => 42)();
This produces the error:
CS0149 Method name expected
There are two workarounds:
Declare a (local) method:
int Return42() => 42;
int i = Return42();
Create a delegate:
int i = ((Func<int>)(() => 42))();
It is possible to create and immediately invoke a lambda expression without creating a delegate and without naming it? And if possible, how can one create such a lambda?
1. In reality it is an async Task that I would like to use instead of Task.ContinueWith (I tried to follow what Stephen Cleary said: you should strive to replace ContinueWith with await); e.g:
Task<int> future = (async () =>
(await RetrieveLookupFromFarAway())["42"].First())();
With RetrieveLookupFromFarAway something like:
async Task<ILookup<string, int>> RetrieveLookupFromFarAway()
{
await Task.Delay(1000);
return Enumerable.Empty<int>().ToLookup((x) => x.ToString());
}
The concept of a lambda expression only exists as source code. It doesn't even have a type in itself (just like the null literal doesn't have a type). It has to be converted to either an expression tree or a delegate: that's what exists as far as the IL and the CLR are concerned. The compiler has to emit code to create something, and you need to tell it which type you want that to be.
The compiler doesn't play favourites in terms of delegate types: while it could "know" about Func<T> and use that as a default delegate type for a lambda expression with no parameters, it doesn't.
The closest you'll get to what you want is to have a convenience method that you can call accepting a Func<T>, which could either just return the function, or execute it and return the result. For example:
public static Func<T> CreateFunc<T>(Func<T> func) => func;
public static T ExecuteFunc<T>(Func<T> func) => func();
Then you can call it as:
CreateFunc(() => 42)();
or
ExecuteFunc(() => 42);
I would like to do following thing in Entity Framework code first:
Data.GroupBy(x => x.Person.LastName)
.Select(x => x.OtherGrouping(...)).Where(...).GroupBy(...)...etc
public static class Extensions
{
public static IQueryable<IGrouping<T, T>> OtherGrouping<T>(this IQueryable<T> collection /**** Here I want to pass consts and lambdas\expressions*****/)
{
// Grouping, filtration and other stuff here
return collection.GroupBy(x => x);
}
}
The problem is .net compiles OtherGrouping method, it doesn't represent like an expression, and couldn't be transformed to the SQL.
I found LinqKit library which suppose to help in such cases, but I coudn't figure out how to apply it in my specific case. It works fine for simple cases, when I just have expression like x => x+2, but I get stucked with return IQueryable. Probably it is possible to write expression tree completely by hand but I don't want to go so deep.
Any ideas how it might be done or where I can read about it?
Here is what I tried to do based on Rob's comment
void Main()
{
AddressBases.GroupBy(x => x.ParentId).Select(x => new {x.Key, Items = x.AsQueryable().OtherGrouping(a => a.Country) }).Dump();
}
public static class Extensions
{
public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
source.GroupBy(keySelector).Expression);
}
}
And I got an exception:
NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.Linq.IGrouping`2[System.String,InsuranceData.EF.AddressBase]] OtherGrouping[AddressBase,String](System.Linq.IQueryable`1[InsuranceData.EF.AddressBase], System.Linq.Expressions.Expression`1[System.Func`2[InsuranceData.EF.AddressBase,System.String]])' method, and this method cannot be translated into a store expression.
Couldn't figure out yet why I have OtherGrouping method in expression tree? OtherGrouping method should just attach another grouping to the expression and pass it to the provider, but not put itself to the tree.
Your current code after the edit is correct - where you're going wrong is a common issue with the syntactic sugar we're given by C#.
If you write the following:
void Main()
{
var res = Containers.OtherGrouping(c => c.ContainerID);
res.Expression.Dump();
res.Dump();
}
public static class Extensions
{
public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
{
return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
source.GroupBy(keySelector).Expression
);
}
}
You'll see that we get the output:
Table(Container).GroupBy(c => c.ContainerID)
And the result executes with an issue, properly grouping by our predicate. However, you're invoking OtherGrouping while inside an expression, when you're passing it to Select. Let's try a simple case without OtherGrouping:
var res = Containers.OtherGrouping(c => c.ContainerID)
.Select(x => new
{
x.Key,
Items = x.GroupBy(c => c.ContainerID),
});
res.Expression.Dump();
What you're providing to Select is an expression tree. That is, the inner GroupBy's method is never actually invoked. If you change the query to x.AsQueryable().OtherGrouping and put a breakpoint in OtherGrouping, it will only be hit the first time.
In addition to that, x is IEnumerable<T>, not IQueryable<T>. Invoking AsQueryable() gives you an IQueryable, but it also gives you a new query provider. I'm not 100% sure as to the inner workings of entity framework, but I'd wager to say that this is invalid - as we no longer have the database as a target for the provider. Indeed, with linq2sql (using LINQPad), we get an error saying .AsQueryable() is not supported.
So, what can you do? Unfortunately there's no clean way to do this. Since the expression tree is already built before OtherGrouping has a chance, it doesn't matter what the body of OtherGrouping is. We'll need to change the expression tree after it's built, but before it's executed.
For that, you'll need to write an expression visitor which will look for .OtherGrouping expression calls, and replace it with Queryable.GroupBy
I am getting an error with the following code.
public async Task<bool> FlushUrlAsync(Uri url, bool recursive, CancellationToken token = default(CancellationToken))
{
_serverPortsConfig.CacheServerPorts
.Select(cacheServerPort => $"http://{url.Host}:{cacheServerPort}{url.AbsolutePath}")
.Aggregate(true, (current, memoryCacheUrl) => current && await FlushUrlAsync(recursive, memoryCacheUrl)); //<--- produces the next error:
// Cannot convert async lambda expression to delegate type
// 'Func<bool, string, bool>'. An async lambda expression may return
// void, Task or Task<T>, none of which are convertible to
// 'Func<bool, string, bool>'.
}
This method calls the following function
private async Task<bool> FlushUrlAsync(bool recursive, string memoryCacheUrl)
{
return await someMagic(); //for clearity I removed the code.
}
It looks a lot like: Convert async lambda expression to delegate type System.Func<T>?, howevery, there solution won't /can't get it to work for me.
I had:
var result = true;
foreach (var cacheServerPort in _serverPortsConfig.CacheServerPorts)
{
var memoryCacheUrl = $"http://{url.Host}:{cacheServerPort}{url.AbsolutePath}";
result = result && await FlushUrlAsync(memoryCacheUrl, recursive);
}
return result;
Then resharper gave me the code that is provided, but just adding the async keyword doesn't work.
.Aggregate(true, async (current, memoryCacheUrl) => current && await FlushUrlAsync(recursive, memoryCacheUrl));
will give me the erorr: the return type of async method must be void, task or Task.
any ideas?
I would personally use the foreach implementation, but answering the concrete question.
Without async the Aggregate overload used has the following signature:
bool Aggregate<bool, string>(bool seed, Func<bool, string, bool> func)
Note the func parameter - it's a method receiving bool and string returning a bool. The important part is that the type of the first argument is the same as the type of the result, as well as the type of seed parameter.
Since async lambda must return a Task derived object. You need a bool result, so let substitute it with Task<bool>:
Task<bool> Aggregate<bool, string>(Task<bool> seed, Func<Task<bool>, string, Task<bool>> func)
which leads to the following solution:
return await _serverPortsConfig.CacheServerPorts
.Select(cacheServerPort => $"http://{url.Host}:{cacheServerPort}{url.AbsolutePath}")
.Aggregate(Task.FromResult(true), async (current, memoryCacheUrl) =>
await current && await FlushUrlAsync(recursive, memoryCacheUrl));
Use ContinueWith with TaskContinuationOptions,
Func<Exception, bool> handlerFunc = (Exception ex) =>
{
ex.HandleExceptionAsync().ContinueWith(async (result) =>
{
// all await operations can be called here
}, System.Threading.Tasks.TaskContinuationOptions.OnlyOnRanToCompletion);
return true;
};
Can someone please explain the difference between these two statements:
Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
.ContinueWith(_ => CompressArchiveAsync());
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();
vs
Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
.ContinueWith(_ =>
{
CompressArchiveAsync();
});
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();
The methodsExtractArchiveAsync(), BackupCurrentDatabaseAsync(), RestoreDatabaseAsync() all return a Task.
Here, the first Continuation returns a Task<Task>. I can then Unwrap() this task to put Continuations on the resultant (inner) task.
The second version doesn't compile. The only different here is the braces around the CompressArchiveAsync().
I am trying to access the resultant (internal) Task to check the Task.Status. If I use the second method, the Task.Status is reporting the result of the BackupCurrentDatabaseAsync() task.
.ContinueWith(_ => CompressArchiveAsync());
is equivalent to:
.ContinueWith(_ =>
{
return CompressArchiveAsync();
});
Notice the return.
Your second code snippet doesn't compile because ContinueWith doesn't return a Task<Task>, but simply a Task, and there's nothing to unwrap.
The following is bound to a Func<Task, Task> (a function that takes a Task and returns a Task)
_ =>
{
return CompressArchiveAsync();
}
But the following is actually bound to an Action<Task> (a function that takes a Task but doesn't return anything):
_ =>
{
CompressArchiveAsync();
}
And the reference to the Task created by CompressArchiveAsync is never returned. Without a reference to it, you can't check the Task's status.
Note that:
ContinueWith<TResult>(Func<Task, TResult>) returns a Task<TResult>
ContinueWith(Action<Task>) returns a Task.
Therefore your ContinueWith(Func<Task, Task>) returns a Task<Task> that you can unwrap, but your ContinueWith(Action<Task>) simply returns a Task.
The difference is in the Lambda Expression syntax.
There are 2 types of Lambdas: Expression Lambdas and Statement Lambdas. Expression Lambdas have no braces and return the result of the expression while Statement Lambdas have braces containing zero or more statements (one of them can be a return statement).
So this Expression Lambda:
_ => CompressArchiveAsync()
Is equivalent to this Statement Lambda:
_ => { return CompressArchiveAsync(); }
So, the difference is that in the first continuation you are returning a task but in the second you are not, it's just a void anonymous delegate. That's why the first continuation is a Task<Task> while the second is just a Task.
Long comment to show 4.5 code.
If you could move to .Net 4.5 than code you are trying to write can be rewritten in more compact way with async/await which is essentially implements all that code internally:
async Task CompleteBackup()
{
await backup.BackupCurrentDatabaseAsync()
await CompressArchiveAsync());
await .....
}
In the first example, you are calling ContinueWith with a Func. Therefore, it will return a Task<T>. The second try will call the ContinueWith overload with an Action, because... well it's an action, it does not return anything. So it will return a simple Task without T.