Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# await X and .Result not equivalent in ToDictionary()?

Tags:

c#

async-await

I want to use ToDictionary on a bunch of Task<MyObject> (see below). This works ok if I use TheTask.Result, but not await

This works:

Dictionary<CarId, Task<Model>> allModelTasks = carIds.ToDictionary(cId =>cId, cId=> GetModelForCar(cId));                                  
await Task.WhenAll(allModelTasks.Values);
Dictionary<CarId, Model> allModels = allModelTasks.ToDictionary(mt => mt.Key, mt => mt.Value.Result);

But if I replace last row with

Dictionary<CarId, Model> allModels = allModelTasks.ToDictionary(mt => mt.Key, async mt => await mt.Value);

I get an error message that says "cant convert from Dict<CarId, Task<Model>> to Dict<CarId, Model>". As I see it, the rows should be equivalent.

(The recommendation from here, seems to be to use await instead of .Result, even after EDIT Task.WhenAll)

like image 769
Cowborg Avatar asked Jan 25 '23 01:01

Cowborg


1 Answers

You seem to be ignoring that to be able to write await in that last line, you also had to add another async. Which means the await isn't "in" your current method - it's inside a new async lambda that, of course, being async returns a Task.

You can't use await inside a lambda here and expect not to still have a task to unwrap. Your first code is fine, and doesn't suffer any of the potential problems that accessing .Result can cause because the Tasks are all known to already be complete.


You could create or copy some form of ToDictionaryAsync extension method into your class, something like:

public static class TaskResultExtensions
{
    public static async Task<Dictionary<TKey, TValue>> ToDictionaryAsync<TKey, TValue>(this IEnumerable<KeyValuePair<TKey, Task<TValue>>> source)
    {
        var dict = new Dictionary<TKey, TValue>();
        foreach (var kvp in source)
        {
            dict.Add(kvp.Key, await kvp.Value);
        }

        return dict;
    }
}

Which would then allow your final line to be

Dictionary<CarId, Model> allModels = await allModelTasks.ToDictionaryAsync();

instead.

like image 96
Damien_The_Unbeliever Avatar answered Jan 30 '23 07:01

Damien_The_Unbeliever