I'm trying to populate my cache asynchronously
static ConcurrentDictionary<string, string[]> data = new ConcurrentDictionary<string, string[]>();
public static async Task<string[]> GetStuffAsync(string key)
{
return data.GetOrAdd(key, async (x) => {
return await LoadAsync(x);
});
}
static async Task<string[]> LoadAsync(string key) {....}
but this gives me the error:
Cannot convert async lambda expression to delegate type 'System.Func'.
An async lambda expression may return void, Task or Task, none of which are convertible to 'System.Func'.
As I understand this is because GetOrAdd()
is not asynchronous. How can I fix the issue?
Update:
LazyAsync
suggested in the comments will work in my trivial example. Or, workaround like this (can definitely live with some overhead it introduces):
public static async Task<string[]> GetStuffAsync(string key)
{
string[] d = null;
if (!data.ContainsKey(key))
d = await LoadAsync(key);
return data.GetOrAdd(key, d);
}
The question then becomes did Microsoft just have no time to update all interfaces to support async or I'm trying to do something deeply wrong (and ConcurrentDictionary
shouldn't have GetOrAddAsync()
) ?
Async methods (or lambda) can only return
void
orTask
orTask<T>
but your lambda returnsstring[]
and thus compiler prevents you.await
keyword is optimized to continue synchronously when the Task is already completed. So, one option is to store the Task itself in dictionary and don't worry about awaiting the completed Task again and again.And when you do
first time it will (a)wait till the cached Task finishes --There after it will continue synchronously.
You'll have to think about what should happen when
LoadAsync
fails. Because we're caching the Task returned byLoadAsync
; if that fails we'll foolishly cache the failed Task. You may need to handle this.