Alex
Alex

Reputation: 5724

How to get an async lambda to await?

I have the following function:

public async Task<List<UserViewModel>> Get(string role, string nameText, int offset, int number) {
    List<User> users = new List<User>();
    if (role != "") {
    new List<User>(await userManager.GetUsersForClaimAsync(new System.Security.Claims.Claim("role", role)));

    }

    List<Task<UserViewModel>> data = users
    .Skip(offset)
    .Take(number)
    .Select(async (user) => {
        return await identityUserToUserViewModel(user); ;
    }).ToList();


    return data;
}

which calls

async Task<UserViewModel> identityUserToUserViewModel(User user) {
    var principal = await userManager.GetClaimsAsync(user);
    return new UserViewModel()
    {
        Email = user.Email,
        PhoneNumber = user.PhoneNumber,
        Location = locationRepository.GetForUser(user.Id),
        Name = user.Name,
        Role = principal.Where(Claim => Claim.Type == "role").ToList().FirstOrDefault().Value,
        sub = user.Id
    };
}

However, this function gives an error of Cannot implicitly convert type 'System.Collections.Generic.List<System.Threading.Tasks.Task<Models.ViewModel.UserViewModel>>' to 'System.Collections.Generic.List<Models.ViewModel.UserViewModel>'

I understand that the async lambda is converting it back into a task, but I can't figure out how to -not- have that happen.

Upvotes: 2

Views: 6777

Answers (2)

ganchito55
ganchito55

Reputation: 3607

Due to the following code returns a Task<UserViewModel> for each call

.Select(async (user) => {
    return await identityUserToUserViewModel(user);

You could wrap the LINQ expression with await Task.WhenAll(...) this returns an array of UserViewModel that can be converted to a List<UserViewModel>

Upvotes: 0

poke
poke

Reputation: 388333

.Select(async (user) => {
    return await identityUserToUserViewModel(user);
})

Because making something async actually means that the result gets wrapped in a Task, this actually has the same result as the following:

.Select((user) => {
    return identityUserToUserViewModel(user);
})

So the result of that call is an IEnumerable<Task<UserViewModel>>. So converting it to a list will give you a list of tasks, not a list of UserViewModels.

If you now want to await all those tasks and just return a list of view models, then you can use Task.WhenAll. It will await all the tasks it gets passed, and fortunately return an array of all the tasks’ results:

IEnumerable<Task<UserViewModel>> dataTasks = users
    .Skip(offset)
    .Take(number)
    .Select(identityUserToUserViewModel);

UserViewModel[] data = Task.WhenAll(dataTasks);

return data;

Upvotes: 3

Related Questions