I have an async method inside a portable class library with this signature:
private async Task<T> _Fetch<T>(Uri uri)
It fetches a resource that is cast back as a concrete type T.
I'm working with a 3rd party cache library ( Akavache ) that requires a Func<T>
as one of the parameters and have tried to do so in this manner:
await this.CacheProvider.GetOrCreateObject<T>(key,
async () => await _Fetch<T>(uri), cacheExpiry);
This results in the error:
Cannot convert async lambda expression to delegate type '
System.Func<T>
'. An async lambda expression may returnvoid
,Task
orTask<T>
, none of which are convertible to 'System.Func<T>
'.
I've tried various permutations of Func<T>
assignment without any luck, the only way I can get the code to work is to make the Func<T>
blocking:
await this.CacheProvider.GetOrCreateObject<T>(key,
() => _Fetch<T>(uri).Result, cacheExpiry);
which deadlocks my app.
Any pointers on where I'm going astray?
No can do. When someone expects a Func<T> f
you can assume it will be invoked with something like result = f()
- ie, it does not know about async behavior. If you cheat it by using .Result
like you have - it will deadlock on UI thread because it wants to schedule the code after await
(in _Fetch) on the UI thread, but you have already blocked it with .Result
.
Async lambda can be passed to Action
since it has no return value - or to Func<Task>
or Func<Task<T>>
.
Looking at your case, the GetOrCreateObject
appears to be calling GetOrFetchObject
. One of the GetOrFetchObject
overloads accepts a Func<Task<T>>
. You can try calling that method with your async lambda and see if it helps.
YK1's answer explains why you can't treat Func<T>
as asynchronous.
To fix your problem, use GetOrFetchObject
instead of GetOrCreateObject
. The "create" methods assume a (synchronous) creation, while the "fetch" methods work with (asynchronous) retrieval.
await CacheProvider.GetOrFetchObject<T>(key, () => _Fetch<T>(uri), cacheExpiry)
I also removed the unnecessary async
/ await
in your lambda expression. Since _Fetch
already returns Task<T>
, there's no need to create an async
lambda whose sole purpose is to await
that task.
Something like this?
Public Func<T> ConvertTask<T>(Task<T> task)
{
return ()=>task.Result;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.