.NET 8 Blazor Server中Identity UI无法保存扩展用户属性
问题原因与解决方案
核心问题
你在OnInitializedAsync中获取的user是**ClaimsPrincipal类型**(用户身份凭证对象),而非数据库中被EF Core跟踪的ApplicationUser实体。直接给ClaimsPrincipal赋值自定义属性不会被EF Core识别为实体变更,自然无法保存到数据库。而电话号码能正常保存,是因为SetPhoneNumberAsync是Identity内置方法,内部会正确获取并更新数据库中的用户实体。
修正步骤
1. 正确获取数据库中的ApplicationUser实体
修改OnInitializedAsync方法,通过UserManager从数据库中加载真正的ApplicationUser实例:
// 先确保user变量的声明为ApplicationUser类型: private ApplicationUser? user; protected override async Task OnInitializedAsync() { var claimsUser = await UserAccessor.GetRequiredUserAsync(HttpContext); // 通过用户ID从数据库获取跟踪的ApplicationUser实体 user = await UserManager.FindByIdAsync(await UserManager.GetUserIdAsync(claimsUser)); if (user == null) { RedirectManager.RedirectToPage("/Account/Login"); return; } username = await UserManager.GetUserNameAsync(user); phoneNumber = await UserManager.GetPhoneNumberAsync(user); Input.PhoneNumber ??= phoneNumber; Input.FirstName = user.FirstName; Input.LastName = user.LastName; }
2. 完善更新逻辑并添加错误检查
在OnValidSubmitAsync中使用数据库实体进行修改,并添加更新结果校验,方便排查问题:
private async Task OnValidSubmitAsync() { if (user == null) return; if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await UserManager.SetPhoneNumberAsync(user, Input.PhoneNumber); if (!setPhoneResult.Succeeded) { RedirectManager.RedirectToCurrentPageWithStatus("错误:设置电话号码失败。", HttpContext); return; } } // 修改跟踪实体的属性 user.FirstName = Input.FirstName; user.LastName = Input.LastName; // 执行更新并检查结果 var updateResult = await UserManager.UpdateAsync(user); if (!updateResult.Succeeded) { var errorMsg = string.Join(",", updateResult.Errors.Select(e => e.Description)); RedirectManager.RedirectToCurrentPageWithStatus($"错误:更新资料失败。{errorMsg}", HttpContext); return; } await SignInManager.RefreshSignInAsync(user); RedirectManager.RedirectToCurrentPageWithStatus("你的资料已更新", HttpContext); }
3. 可选:添加属性验证(提升用户体验)
给ApplicationUser和InputModel的自定义属性添加验证特性,确保输入有效性:
// ApplicationUser.cs using System.ComponentModel.DataAnnotations; public class ApplicationUser : IdentityUser { [Required] [StringLength(50)] public string FirstName { get; set; } = string.Empty; [Required] [StringLength(50)] public string LastName { get; set; } = string.Empty; }
// Index.razor中的InputModel private sealed class InputModel { [Phone] [Display(Name = "Phone number")] public string? PhoneNumber { get; set; } [Required] [StringLength(50)] public string FirstName { get; set; } = string.Empty; [Required] [StringLength(50)] public string LastName { get; set; } = string.Empty; }
内容的提问来源于stack exchange,提问作者Stian
相关产品推荐
相关产品推荐

