Reputation: 5720
I have a method that gets a Task<T>
where T is unknown at compile time and an IAsyncDisposable
. I need to return a Task<T>
which automatically disposes the disposable object after the original task has been completed.
This is what I came up with so far, but it is throwing a compile time Error
private static TResult AutoDispose<TResult>(TResult result, IAsyncDisposable asyncDisposable)
{
Func<dynamic, dynamic> continuationFunction = async t => { await asyncDisposable.DisposeAsync(); return ((dynamic)t).Result; };
var autoDisposing = ((dynamic)result).ContinueWith(continuationFunction);
return (TResult)autoDisposing;
}
The Error, I'm getting is
Cannot convert async lambda expression to delegate type 'Func<dynamic, dynamic>'. An async lambda expression may return void, Task or Task, none of which are convertible to 'Func<dynamic, dynamic>'.
I already tried different combinations of dynamic
and Task
but was unable to create a working solution. I'm always getting compile- or runtime errors.
EDIT
Because it seems to be confusing why I'm doing it this way:
I'm using this inside an IAsyncQueryProvider
s ExecuteAsync
method. The Interface defines the methods Signature as following
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
Based on this, I know TResult
is either of type IAsyncEnumerable<T>
or Task<T>
. I already wrote code to handle the case when it is an IAsyncEnumerable<T>
but I'm still struggling when it is a Task<T>
.
Upvotes: 1
Views: 414
Reputation: 456407
To use async
, the compile-time result type must be Task
(or a tasklike). Nested tasks are normal using ContinueWith
with async
continuations; Unwrap
is one solution. If you can avoid ContinueWith
completely, that's even better, but I believe in this case that would require quite a bit of tedious generics work.
Something like this should work:
private static TResult AutoDispose<TResult>(TResult result, IAsyncDisposable asyncDisposable) // where TResult: Task<T>
{
Func<dynamic, Task<TResult>> continuationFunction = async () => { await asyncDisposable.DisposeAsync(); return result; };
var continuation = ((dynamic)result).ContinueWith(continuationFunction, TaskScheduler.Default);
var newResult = TaskExtensions.Unwrap(continuation);
return (TResult)newResult;
}
Upvotes: 3
Reputation: 155055
Try this?
private static async Task<TResult> AutoDispose<TResult>( this Task<TResult> task )
where TResult : IAsyncDisposable
{
TResult result = await task.ConfigureAwait(false);
if( result != null )
{
await result.DisposeAsync().ConfigureAwait(false);
}
return result;
}
Example usage:
Task<HttpResponseMessage> task = new HttpClient()
.GetAsync( "/foo" )
.AutoDispose();
HttpResponseMessage resp = await task;
// `resp` will be in a disposed state at this point.
Upvotes: 0