.NET Core 2.0 MVC中如何扩展UserManager以在_LoginPartial获取Name
解决在_LoginPartial视图中显示ApplicationUser的Name字段问题
我来给你两种实用的方案,都能达到你要的效果,你可以根据自己的场景选择:
方案一:扩展UserManager类,添加GetName方法
这种方法和UserManager.GetUserName的使用方式完全一致,适合需要实时从数据库获取最新Name的场景(比如用户可能随时修改Name)。
步骤1:创建UserManager扩展类
在你的项目里新建一个静态类(比如UserManagerExtensions.cs),写入以下代码:
using Microsoft.AspNetCore.Identity; public static class UserManagerExtensions { // 同步版本 public static string GetName(this UserManager<ApplicationUser> userManager, ApplicationUser user) { // 如果Name为空,回退到UserName,避免显示空白 return user?.Name ?? userManager.GetUserName(user); } // 异步版本(更符合Identity的异步设计) public static async Task<string> GetNameAsync(this UserManager<ApplicationUser> userManager, ApplicationUser user) { if (user == null) return null; // 如果你担心用户对象是缓存的,也可以先从数据库重新获取 // var freshUser = await userManager.FindByIdAsync(user.Id); // return freshUser?.Name ?? await userManager.GetUserNameAsync(user); return user.Name ?? await userManager.GetUserNameAsync(user); } }
步骤2:在_LoginPartial视图中使用
首先在视图顶部注入UserManager和SignInManager,然后获取当前用户并调用扩展方法:
@inject UserManager<ApplicationUser> UserManager @inject SignInManager<ApplicationUser> SignInManager @if (SignInManager.IsSignedIn(User)) { var currentUser = await UserManager.GetUserAsync(User); var displayName = await UserManager.GetNameAsync(currentUser); <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @displayName!</a> </li> <li class="nav-item"> <form class="form-inline" asp-area="" asp-controller="Account" asp-action="Logout"> <button type="submit" class="nav-link btn btn-link text-dark">Logout</button> </form> </li> } else { <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a> </li> <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Register">Register</a> </li> }
方案二:通过ClaimsPrincipal扩展直接获取Name(性能更优)
这种方法把Name存入用户的Claim中,不需要每次查询数据库,适合Name不经常修改的场景,使用起来更简洁。
步骤1:自定义ClaimsPrincipal工厂
新建一个类CustomUserClaimsPrincipalFactory.cs,用来把Name添加到用户的Claim集合里:
using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using System.Security.Claims; using System.Threading.Tasks; public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser> { public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user) { var identity = await base.GenerateClaimsAsync(user); // 如果Name不为空,就添加到Claim里 if (!string.IsNullOrEmpty(user.Name)) { identity.AddClaim(new Claim(ClaimTypes.GivenName, user.Name)); } return identity; } }
步骤2:在Startup.cs中注册自定义工厂
找到ConfigureServices方法,添加以下注册代码:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
步骤3:创建ClaimsPrincipal扩展方法
新建静态类ClaimsPrincipalExtensions.cs:
using System.Security.Claims; public static class ClaimsPrincipalExtensions { public static string GetName(this ClaimsPrincipal principal) { // 先找我们添加的GivenName Claim,找不到就回退到默认的Name(也就是UserName) var nameClaim = principal.FindFirst(ClaimTypes.GivenName) ?? principal.FindFirst(ClaimTypes.Name); return nameClaim?.Value ?? string.Empty; } }
步骤4:在视图中直接使用
现在视图里不需要注入UserManager了,直接调用扩展方法即可:
@inject SignInManager<ApplicationUser> SignInManager @if (SignInManager.IsSignedIn(User)) { <li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello @User.GetName()!</a> </li> // ... 其他登出代码 } else { // ... 登录注册代码 }
两种方案对比
- 方案一:实时获取数据库中的Name,适合Name经常变动的场景,但每次都要查询数据库(可以通过缓存优化)。
- 方案二:从Cookie中的Claim读取,性能更好,但Name修改后需要用户重新登录才能更新显示。
内容的提问来源于stack exchange,提问作者user6383418
相关产品推荐
相关产品推荐

