我正在尝试使用实体框架从表中执行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"除非我通过引用传递异步方法不允许的内容。
有人能解释一下正确填充名称属性的最有效方法吗?
答案 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