Tôi đang sử dụng Asp.Net Core Identity và cố gắng đơn giản hóa một số mã dự án danh sách người dùng và vai trò của họ cho ViewModel. Mã này hoạt động, nhưng trong cố gắng đơn giản hóa nó tôi đã đi vào một xoắn ốc điên rồ của các lỗi và sự tò mò.Sử dụng async/await bên trong .Chọn lambda
Đây là mã làm việc của tôi:
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);
}
Trong cố gắng để đơn giản hóa mã, I figured tôi có thể làm một cái gì đó giống như (mã bị hỏng) này:
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();
này ngắt vì tôi không sử dụng từ khóa không đồng bộ trong biểu thức lambda trước người dùng. Tuy nhiên, khi tôi làm thêm async trước dùng, tôi nhận được thêm một lỗi mà nói "Async biểu thức lambda không thể được chuyển đổi sang cây khái niệm"
tôi đoán là phương pháp GetRolesAsync() đang trả lại công tác và gán nó cho Vai trò thay vì kết quả thực tế của tác vụ đó. Những gì tôi không thể hình dung ra cho cuộc sống của tôi là làm thế nào để làm cho nó hoạt động.
Tôi đã nghiên cứu và thử nhiều phương pháp trong ngày qua mà không có may mắn. Dưới đây là một vài mà tôi nhìn:
Is it possible to call an awaitable method in a non async method?
https://blogs.msdn.microsoft.com/pfxteam/2012/04/12/asyncawait-faq/
Calling async method in IEnumerable.Select
How to await a list of tasks asynchronously using LINQ?
how to user async/await inside a lambda
How to use async within a lambda which returns a collection
Phải thừa nhận rằng, tôi hoàn toàn không hiểu cách hoạt động của đồng bộ/chờ đợi, vì vậy đó có thể là một phần của vấn đề. Mã foreach của tôi hoạt động, nhưng tôi muốn có thể hiểu cách làm cho nó hoạt động theo cách tôi đang cố gắng. Vì tôi đã dành rất nhiều thời gian cho nó nên tôi đã nghĩ đây sẽ là một câu hỏi hay.
Cảm ơn!
Sửa
Tôi đoán tôi phải giải thích những gì tôi đã làm trong mỗi trường hợp của các bài viết tôi nghiên cứu để cho điều này để không bị gắn cờ là một câu hỏi trùng lặp - và tôi đã cố gắng thực sự khó khăn để tránh điều đó: - /. Trong khi câu hỏi có vẻ tương tự, kết quả thì không. Trong trường hợp của bài viết đã được đánh dấu là một câu trả lời tôi đã cố gắng đoạn mã sau:
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))
};
}
Tôi cũng đã cố gắng sử dụng AsEnumerable như vậy:
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();
Cả hai sản xuất thông báo lỗi: " InvalidOperationException: Một thao tác thứ hai bắt đầu trên ngữ cảnh này trước khi một thao tác trước đó hoàn thành.Bất kỳ thành viên cá thể nào cũng không được bảo đảm là chuỗi an toàn. "
Tại thời điểm này có vẻ như ForEach ban đầu của tôi có thể là đặt cược tốt nhất, nhưng tôi vẫn tự hỏi điều gì sẽ là đúng cách để làm điều này nếu tôi là để làm điều đó bằng cách sử dụng phương pháp async
chỉnh sửa 2 - với lời Nhờ comments Tseng (và một số nghiên cứu khác) tôi đã có thể làm cho mọi việc làm việc bằng cách sử dụng đoạn mã sau:.
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());
Mặc dù bây giờ tôi đã lấy tất cả mọi người ý kiến xem xét, tôi bắt đầu nhìn gần hơn tại SQL Profiler chỉ để xem có bao nhiêu lượt truy cập DB thực sự nhận được - như Matt Johnson đã đề cập, nó rất nhiều (N + 1).
Vì vậy, trong khi điều này trả lời câu hỏi của tôi, bây giờ tôi xem xét lại cách chạy truy vấn và có thể chỉ thả vai trò trong chế độ xem chính và chỉ kéo chúng khi mỗi người dùng được chọn. Tôi chắc chắn đã học được rất nhiều thông qua câu hỏi này mặc dù (và tìm hiểu thêm về những gì tôi không biết), vì vậy cảm ơn tất cả mọi người.
'Tôi đoán là phương thức GetRolesAsync() đang trả về một nhiệm vụ và gán nó cho vai trò thay vì kết quả thực tế của nhiệm vụ đó.'- có lẽ không, bởi vì' await' sẽ lấy kết quả của nhiệm vụ. – mason
Hãy thử đặt 'AsEnumerable' trước' Select' để nó sẽ được chạy trong LInq to Objects thay vì cố chuyển đổi nó thành cây biểu thức cho EF hoặc bất kỳ nhà cung cấp nào bạn sử dụng. – juharr
var usersViewModels = (chờ Task.WhenAll (allUsers.AsEnumerable(). Chọn (user async => mới 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 ("", chờ đợi _userManager.GetRolesAsync (user)) }))). Liệt kê(); –