Fra*_*ain 11 c# lambda asynchronous asp.net-identity asp.net-core
我正在使用Asp.Net Core Identity并尝试简化将一组用户及其角色投影到ViewModel的代码.这段代码有效,但在尝试简化它时,我陷入了一个疯狂的错误和好奇心.
这是我的工作代码:
var allUsers = _userManager.Users.OrderBy(x => x.FirstName);
var usersViewModel = new List<UsersViewModel>();
foreach (var user in allUsers)
{
var tempVm = new UsersViewModel()
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled,
Roles = String.Join(", ", await _userManager.GetRolesAsync(user))
};
usersViewModel.Add(tempVm);
}
Run Code Online (Sandbox Code Playgroud)
在尝试简化代码时,我想我可以做这样的事情 (破碎的代码):
var usersViewModel = allUsers.Select(user => new UsersViewModel
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled,
Roles = string.Join(", ", await _userManager.GetRolesAsync(user))
}).ToList();
Run Code Online (Sandbox Code Playgroud)
这会中断,因为我没有在用户之前使用 lambda表达式中的async关键字.但是,当我在用户之前添加异步时,我得到另一个错误,上面写着"异步lambda表达式无法转换为表达式树"
我的猜测是GetRolesAsync()方法返回一个Task并将其分配给Roles而不是该任务的实际结果.我似乎无法弄清楚我的生活是如何让它发挥作用.
我在过去的一天里研究并尝试了很多方法而没有运气.以下是我看过的一些内容:
https://blogs.msdn.microsoft.com/pfxteam/2012/04/12/asyncawait-faq/
不可否认,我并不完全理解async/await是如何工作的,所以这可能是问题的一部分.我的foreach代码有效,但我希望能够理解如何让它按照我想要的方式工作.因为我已经花了这么多时间,所以我认为这将是一个很好的第一个问题.
谢谢!
编辑
我想我必须解释我在每个研究过的文章中所做的事情,以便不将其标记为重复的问题 - 我努力避免这样做: - /.虽然这个问题听起来很相似,但结果并非如此.对于标记为答案的文章,我尝试了以下代码:
public async Task<ActionResult> Users()
{
var allUsers = _userManager.Users.OrderBy(x => x.FirstName);
var tasks = allUsers.Select(GetUserViewModelAsync).ToList();
return View(await Task.WhenAll(tasks));
}
public async Task<UsersViewModel> GetUserViewModelAsync(ApplicationUser user)
{
return new UsersViewModel
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled,
Roles = String.Join(", ", await _userManager.GetRolesAsync(user))
};
}
Run Code Online (Sandbox Code Playgroud)
我也试过像这样使用AsEnumerable:
var usersViewModel = allUsers.AsEnumerable().Select(async user => new UsersViewModel
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled,
Roles = string.Join(", ", await _userManager.GetRolesAsync(user))
}).ToList();
Run Code Online (Sandbox Code Playgroud)
这两个都会产生错误消息:"InvalidOperationException:在上一个操作完成之前,在此上下文中启动了第二个操作.不保证所有实例成员都是线程安全的."
在这一点上,似乎我的原始ForEach可能是最好的选择,但我仍然想知道如果我使用异步方法做这件事的话,这是正确的方法.
编辑2 - 回答 感谢Tseng的评论(和其他一些研究)我能够使用以下代码使事情有效:
var userViewModels = allUsers.Result.Select(async user => new UsersViewModel
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled,
Roles = string.Join(", ", await _userManager.GetRolesAsync(user))
});
var vms = await Task.WhenAll(userViewModels);
return View(vms.ToList());
Run Code Online (Sandbox Code Playgroud)
虽然现在我已经考虑了每个人的评论,但我开始仔细研究SQL Profiler,看看数据库实际得到了多少命中 - 正如Matt Johnson提到的那样,它很多(N + 1).
因此,虽然这确实回答了我的问题,但我现在正在重新考虑如何运行查询,并且可能只是在主视图中删除角色,并且只在选择每个用户时拉出它们.我通过这个问题肯定学到了很多东西(并且学到了更多我不知道的东西),谢谢大家.
Tse*_*eng 11
我想你在这里混合了两件事.表达树和代表.Lambda可用于表示它们,但它取决于方法接受的参数类型,它将转向哪一个.
一个lambda传递给作为方法Action<T>
或Func<T, TResult>
将被转换成一个代表(基本上匿名功能/方法).
将lambda表达式传递给方法接受时Expression<T>
,可以从lambda创建表达式树.表达式树只是描述代码的代码,但不是代码本身.
话虽如此,表达式树无法执行,因为它已转换为可执行代码.您可以在运行时编译表达式树,然后像委托一样执行它.
ORM框架使用表达式树来允许您编写可以翻译成不同内容的"代码"(例如数据库查询)或在运行时动态生成代码.
因此,您不能async
在接受的方法中使用Expression<T>
.它转换为它时可能起作用的原因AsEnumerable()
是因为它返回一个IEnumerable<T>
并且它上面的LINQ方法接受Func<T, TResult>
.但它基本上取出整个查询并在内存中完成所有内容,因此您无法使用投影(或者您只需在使用表达式和投影之前获取数据),将过滤后的结果转换为列表然后对其进行过滤.
你可以尝试这样的事情:
// Filter, sort, project it into the view model type and get the data as a list
var users = await allUsers.OrderBy(user => user.FirstName)
.Select(user => new UsersViewModel
{
Id = user.Id,
UserName = user.UserName,
FirstName = user.FirstName,
LastName = user.LastName,
DisplayName = user.DisplayName,
Email = user.Email,
Enabled = user.Enabled
}).ToListAsync();
// now that we have the data, we iterate though it and
// fetch the roles
var userViewModels = users.Select(async user =>
{
user.Roles = string.Join(", ", await _userManager.GetRolesAsync(user))
});
Run Code Online (Sandbox Code Playgroud)
第一部分将完全在数据库上完成,您将保留所有优势(即订单发生在数据库中,因此您不必在获取结果后进行内存中排序,限制调用限制从数据库获取的数据DB等).
第二部分遍历内存中的结果并获取每个临时模型的数据,最后将其映射到视图模型中.
归档时间: |
|
查看次数: |
8247 次 |
最近记录: |