如何在lambda中选择期间等待

时间:2016-10-20 15:30:31

标签: c# lambda async-await

我正在尝试使用实体框架从表中执行Lambda Select到新模型,但我需要能够调用异步方法来填充返回集中每个实例的属性:

await Task.WhenAll(_context.UserContacts.Where(uc => uc.UserId == user.Id).Select(async uc => new MailContact
            {
                Email = uc.Contact.Email,
                UserId = uc.Contact.UserId,
                ContactId = uc.Contact.Id,
                Name = uc.Contact.UserId != null ? await _graphService.GetUserByIdAsync(uc.Contact.UserId) : null;
            }

我知道Linq对await / async的支持有限,我已经看了StackOverflow上的其他几个例子,其中异步部分被移动到一个单独的循环中,例如:

How to await a method in a Linq query

T[] data = await Task.WhenAll(contacts.Select(c => LoadDataAsync(c)));

然而,这种方法不会让我更新被称为" c"除非我通过引用传递异步方法不允许的内容。

有人能解释一下正确填充名称属性的最有效方法吗?

2 个答案:

答案 0 :(得分:5)

你只需要在LINQ-to-Objects中进行二次查找,而不是LINQ-to-Entities:

// LINQ-to-Entities
var users = await _context.UserContacts
    .Where(uc => uc.UserId == user.Id)
    .Select(uc => new
    {
      uc.Contact.Email,
      uc.Contact.UserId,
      ContactId = uc.Contact.Id,
    })
    .ToListAsync();

// LINQ-to-Objects
var lookupTasks = users.Select(async u => new
    {
      u.Email,
      u.UserId,
      u.ContactId,
      Name = u.UserId != null ? await _graphService.GetUserByIdAsync(u.UserId) : null;
    });
return await Task.WhenAll(lookupTasks);

这个想法是你将尽可能多的逻辑推入最初的LINQ-to-Entities查询,包括过滤和投影。然后执行它(ToListAsync)。

然后您获取数据库结果并进行二次查找。

答案 1 :(得分:0)

我通过创建包装器解决了这个问题:

    private async Task<string> GetDisplayName(string userId)
    {
        if (string.IsNullOrEmpty(userId)) return null;
        var contactUser = await _graphService.GetUserByIdAsync(userId);
        return contactUser.DisplayName;
    }

并将作业更改为:

    Name = u.UserId != null ? await GetDisplayName(u.UserId) : null