Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to hydrate a Dictionary with the results of async calls?

Tags:

Suppose I have code that looks like this:

public async Task<string> DoSomethingReturnString(int n) { ... } int[] numbers = new int[] { 1, 2 , 3}; 

Suppose that I want to create a dictionary that contains the result of calling DoSomethingReturnString for each number similar to this:

Dictionary<int, string> dictionary = numbers.ToDictionary(n => n,     n => DoSomethingReturnString(n)); 

That won't work because DoSomethingReturnString returns Task<string> rather than string. The intellisense suggested that I try specifying my lambda expression to be async, but this didn't seem to fix the problem either.

like image 605
Vivian River Avatar asked Jun 13 '16 17:06

Vivian River


People also ask

What happens when you call async method?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

Does async await improve performance?

C# Language Async-Await Async/await will only improve performance if it allows the machine to do additional work.

Does async await run synchronously?

Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.

What are the async and await keywords?

The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.


2 Answers

If you insist on doing it with linq, Task.WhenAll is the key to "hydrate" the dictionary:

int[] numbers = new int[] { 1, 2 , 3};  KeyValuePair<int, string>[] keyValArray = //using KeyValuePair<,> to avoid GC pressure     await Task.WhenAll(numbers.Select(async p =>          new KeyValuePair<int, string>(p, await DoSomethingReturnString(p))));  Dictionary<int, string> dict = keyValArray.ToDictionary(p => p.Key, p => p.Value); 
like image 62
shay__ Avatar answered Oct 03 '22 19:10

shay__


LINQ methods do not support asynchronous actions (e.g., asynchronous value selectors), but you can create one yourself. Here is a reusable ToDictionaryAsync extension method that supports an asynchronous value selector:

public static class ExtensionMethods {     public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TInput, TKey, TValue>(         this IEnumerable<TInput> enumerable,         Func<TInput, TKey> syncKeySelector,         Func<TInput, Task<TValue>> asyncValueSelector)     {         Dictionary<TKey,TValue> dictionary = new Dictionary<TKey, TValue>();          foreach (var item in enumerable)         {             var key = syncKeySelector(item);              var value = await asyncValueSelector(item);              dictionary.Add(key,value);         }          return dictionary;     } } 

You can use it like this:

private static async Task<Dictionary<int,string>>  DoIt() {     int[] numbers = new int[] { 1, 2, 3 };      return await numbers.ToDictionaryAsync(         x => x,         x => DoSomethingReturnString(x)); } 
like image 31
Yacoub Massad Avatar answered Oct 03 '22 19:10

Yacoub Massad