我的登录方法如下,您会看到在使用
_userManager.FindByEmailAsync()
获取用户后,我必须再次调用我的用户,因为有几个字段 Points
和 Tokens
计算为 Sum()
问题 - 有什么方法可以扩展
_userManager.FindByEmailAsync()
,以便它像我在Sum
调用中那样运行.Select()
,这样我就不会对数据库进行冗余调用?
我尝试打电话:
var userFromRepo = await _userManager.Users.Select(p => new Core.Entities.User { Id = p.Id, Email = p.Email, UserName = p.UserName}).SingleOrDefaultAsync(p => p.Email == loginDto.Email);
并将其传递给
CheckPasswordSignInAsync()
但结果失败,因此它正在寻找除电子邮件、用户名或 ID 之外的其他内容。但什么?
如果我只是调用它来返回整个
User
:
var userFromRepo = await _userManager.Users.SingleOrDefaultAsync(p => p.Email == loginDto.Email);
它可以工作,但我无法运行
Sum()
调用 Point
和 Token
这是我的登录方法。
[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> Login(LoginDto loginDto) {
var userFromRepo = await _userManager.FindByEmailAsync(loginDto.Email);
if (userFromRepo == null) return Unauthorized(new ApiResponse(401));
if (userFromRepo.IsActive == false) return NotFound(new ApiResponse(404, "This account is no longer active."));
var result = await _signInManager.CheckPasswordSignInAsync(userFromRepo, loginDto.Password, false);
if (!result.Succeeded) return Unauthorized(new ApiResponse(401));
var user = await _dbContext.Users
.Select(p => new {
Id = p.Id,
Email = p.Email,
UserName = p.UserName,
Hosted = p.Hosted,
Instructed = p.Instructed,
Attended = p.Attended,
IsBoarded = p.IsBoarded,
IsActive = p.IsActive,
Likers = p.LikersCount,
Rating = p.Rating,
YearStarted = p.YearStarted,
YearsInstructing = p.YearsInstructing,
YearsPracticing = System.DateTime.Now.Year - p.YearStarted,
Certification = p.Certification.ToString(),
CreatedDate = p.CreatedDate,
PhotoUrl = p.IsBoarded ? p.UserPhoto : "assets/images/user.png",
Age = p.DateOfBirth.CalculateAge(),
DateOfBirth = p.DateOfBirth,
ExperienceLevel = p.ExperienceLevel.GetEnumName(),
Points = p.UserPoints.Sum(p => p.Points),
Tokens = p.Tokens.Sum(p => p.Tokens),
Token = _tokenService.CreateToken(userFromRepo, (from userRole in p.UserRoles join role in _dbContext.Roles on userRole.RoleId equals role.Id select role.Name).ToList()),
IsInstructor = p.IsInstructor
})
.FirstOrDefaultAsync(p => p.Id == userFromRepo.Id);
if (user == null) return NotFound(new ApiResponse(404));
return Ok(user);
}
如果要扩展
_userManager.FindByEmailAsync
方法,则需要创建一个继承 IUserEmailStore
的自定义类,因为 FindByEmailAsync
方法将调用 IUserEmailStore 的 FindByEmailAsync 方法。
_userManager
的源代码是这样的:
public virtual async Task<TUser?> FindByEmailAsync(string email)
{
ThrowIfDisposed();
IUserEmailStore<TUser> store = GetEmailStore();
Microsoft.AspNetCore.Shared.ArgumentNullThrowHelper.ThrowIfNull(email, "email");
email = NormalizeEmail(email);
TUser val = await store.FindByEmailAsync(email, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (val == null && Options.Stores.ProtectPersonalData)
{
ILookupProtectorKeyRing service = _services.GetService<ILookupProtectorKeyRing>();
ILookupProtector protector = _services.GetService<ILookupProtector>();
if (service != null && protector != null)
{
foreach (string allKeyId in service.GetAllKeyIds())
{
string normalizedEmail = protector.Protect(allKeyId, email);
val = await store.FindByEmailAsync(normalizedEmail, CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
if (val != null)
{
return val;
}
}
}
}
return val;
}
在新实现的 IUserEmailStore 类中,您可以修改 FindByEmailAsync,如下所示:
_dbContext.Users
.Where(u => u.Email == email)
.Select(p => new UserDto
{
Id = p.Id,
Email = p.Email,
UserName = p.UserName,
Hosted = p.Hosted,
Instructed = p.Instructed,
Attended = p.Attended,
IsBoarded = p.IsBoarded,
IsActive = p.IsActive,
Likers = p.LikersCount,
Rating = p.Rating,
YearStarted = p.YearStarted,
YearsInstructing = p.YearsInstructing,
YearsPracticing = DateTime.Now.Year - p.YearStarted,
Certification = p.Certification.ToString(),
CreatedDate = p.CreatedDate,
PhotoUrl = p.IsBoarded ? p.UserPhoto : "assets/images/user.png",
Age = p.DateOfBirth.CalculateAge(),
DateOfBirth = p.DateOfBirth,
ExperienceLevel = p.ExperienceLevel.GetEnumName(),
Points = p.UserPoints.Sum(up => up.Points),
Tokens = p.Tokens.Sum(t => t.Tokens),
Token = _tokenService.CreateToken(p,
(from userRole in p.UserRoles
join role in _dbContext.Roles on userRole.RoleId equals role.Id
select role.Name).ToList()),
IsInstructor = p.IsInstructor
})
.FirstOrDefaultAsync();
然后在程序.cs里面注册:
builder.Services.AddTransient<IUserEmailStore<ApplicationUser>, CustomUserEmailStore>();
更详细的使用方法,可以参考这篇文章。