How can I produce a Task<Task> to Unwrap - c#

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.

Related

Return Task<IReadOnlyCollection<T>> from EF Core query without await?

I like to be as specific as is possible with my types. When offering up a method encapsulating a query it's common to see List<T> returned (or more specifically Task<List<T>>.) No wonder with EF Core's .ToListAsync() method. But that's not very accurate is it? List<T> is a projection of a larger collection in SQL Server (or whatever persistence you're using). It's not modifiable.
I want to instead return IReadOnlyCollection<T> from my methods. This seems a lot more accurate. I'm returning a projection from my source data and you can't modify the source data by modifying the projection. Nor can my result be modified erroneously without casting back to List<T> at which point you've gone far enough out of your way I'm assuming you know what you're doing.
So I attempted to just return the interface like I would synchronously.
public Task<IReadOnlyCollection<TResult>> GetResultAsync() =>
_dbContext.Where(x => x.Property == true).ToListAsync();
I'm avoiding using the async and await operators here. I want to pass the Task unawaited to avoid the await overhead. But when wrapped in Task<T> C#'s type system doesn't recognize that the wrapped List<T> is castable to IReadOnlyCollection<T>. So Task<List<T>> cannot be cast to Task<IReadOnlyCollection<T>>. You can try an explicit cast, of course, but the compiler won't like that either and for the same reasons.
Well that's annoying. But we can peel off the Task<T> with async wait easy enough.
public async Task<IReadOnlyCollection<TResult>> GetResultAsync() =>
await _dbContext.Where(x => x.Property = true).ToListAsync();
In order to avoid the added code I considered an extension method.
public static async Task<IReadOnlyCollection<T>> ToReadOnlyCollectionAsync<T>(this IQueryable<T> query) =>
await query.ToListAsync();
This gets the await out of my methods but of course still adds another await. Is there a way to return Task<IReadOnlyCollection<T>> for "free" from EF Core 2.1?
No idea what ToListAsync() does but going by the name, and if I'm guessing correctly, it iterates the results and converts them to a list on a background thread and returns the task that must be awaited before the list is produced.
If that's the case, your question is
how to convert Task<List<T>> to Task<IReadonlyCollection<T>>.
If so, you could in theory do something like this:
public Task<IReadOnlyCollection<string>> ToReadOnlyCollection()
{
return ToListAsync().ContinueWith(x => new ReadOnlyCollection<string>(x.Result) as IReadOnlyCollection<string>);
}
public Task<IReadOnlyList<string>> ToReadOnlyList()
{
// if you don't mind an IReadOnlyList, you can use this
// one which doesn't involve creating a new collection
return ToListAsync().ContinueWith(x => x.Result as IReadOnlyList<string>);
}
private Task<List<string>> ToListAsync()
{
return Task.Run(() =>
{
Task.Delay(1000);
return new List<string>
{
"1",
"2",
"3"
};
});
}
I don't believe Task<T> allows contra-variance so a Task<TChild> (where Child : Parent) cannot be automatically converted to Task<TParent> so you'll need to do it yourself.

Invoke a lambda without creating a delegate; causes CS0149 Method name expected

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);

Why does ContinueWith return Task<Task<x>>

I have a method, GetSomethingAsync, that is return Task<MyTypeA>.Run(() => GetSomething());
I have another method, GetSomethingElseAsync, that is return Task<MyTypeB>.Run(() => GetSomethingElse());
I want the second task to run conditionally, based on something from the first, so I have
var task1 = GetSomethingAsync();
var task2 = task1.ContinueWith(x =>
x.Result == null ? Task.FromResult(null) : GetSomethingElseAsync());
task2 compiles as a Task<Task<MyTypeB>>. I was expecting Task<MyTypeB>. Is it possible to get my expected result?
Assuming
var task1 = GetSomethingAsync();
is defined with async and returns a Task<T>.
And the signature of ContinueWith() is
public Task<TResult> ContinueWith<TResult>(
Func<Task, TResult> continuationFunction
)
Now focusing on your lambda result:
x => x.Result == null ? Task.FromResult(null)
Reads something like, if the Result of the task is null then return a Task (Task.FromResult).
So your Func<Task, TResult> the TResult is a Task<T> as if the lambda was written as:
Task<T> AnonymousFunction(task x)
{
return ... Task.FromResult(null);
}
Now the result of a ContinueWith() is a Task<TResult> and since we've determine a TResult is a Task<T> then the result type is Task<Task<T>>.
What you need is the Unwrap extension method. This method takes a nested task Task<Task<T>>, and flattens it to a Task<T>. The unwrapped task represents the completion of both the outer and inner task. If anyone of them fail, the unwrapped task contains the Exception that occurred in either task:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.ContinueWith(t =>
{
return t.Result is null ?
Task.FromResult<MyTypeB>(null) : GetSomethingElseAsync();
}).Unwrap();
Unfortunately the above code is far from perfect.
It violates the guideline CA2008: Do not create tasks without passing a TaskScheduler.
It propagates the cancellation of the task1 as a failure. So if the task1.IsCanceled, the task2 will be IsFaulted.
It wraps a possible exception of task1 in a nested AggregateException.
Here is the same code corrected:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.ContinueWith(t =>
{
if (t.IsFaulted)
return Task.FromException<MyTypeB>(t.Exception.InnerException);
if (t.IsCanceled)
{
TaskCompletionSource<MyTypeB> tcs = new();
tcs.SetCanceled(new TaskCanceledException(t).CancellationToken);
return tcs.Task;
}
return t.Result is null ?
Task.FromResult<MyTypeB>(null) : GetSomethingElseAsync();
}, TaskScheduler.Default).Unwrap();
A simpler solution is to ditch the cumbersome ContinueWith method, in favor of the handy async/await language feature:
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = ((Func<Task<MyTypeB>>)(async () =>
{
MyTypeA result = await task1.ConfigureAwait(false);
return result is null ? null :
await GetSomethingElseAsync().ConfigureAwait(false);
}))();
This can be simplified even further by using the Then family of extension methods that can be found here (by Stephen Toub).
Task<MyTypeA> task1 = GetSomethingAsync();
Task<MyTypeB> task2 = task1.Then(async result =>
result is null ? null : await GetSomethingElseAsync().ConfigureAwait(false));
You can think the Then extension method as a ContinueWithResult, with an argument that contains the result of the antecedent task, instead of the antecedent task itself.
I believe this is what you are looking for:
var task2 = task1.ContinueWith(completedTask => completedTask.Result == null ? (TypeB)null : GetSomethingElse());
task2 should now be of type Task<MyTypeB>.
The issue is that task1.ContinueWith already returns a Task<T>. In your statement, you are also returning a Task<T>, or more accurately, a Task<MyTypeB>. Hence, the result of the call to task1.ContinueWith is Task<T> where T is Task<MyTypeB>. What you see in the debugger: Task<Task<MyTypeB>>.

Cannot convert async lambda expression to delegate type 'Func<bool, string, bool>'

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;
};

LINQ Select Statement. Anonymous Method Returns Exception

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();
}));

Categories

Resources