ASP.NET Core中如何缓存按用户角色区分的导航栏链接?
问题根因
你原来的<cache>标签写法确实没有解决数据库查询开销的问题:因为获取用户姓名、角色的查询逻辑写在了缓存标签的外层,哪怕缓存命中,这部分数据库查询还是会每次请求都执行。
可行优化方案
方案1:优先使用Claims存储用户基础信息(推荐,无需额外缓存)
这是成本最低、效果最好的方案,可避免所有相关数据库查询:
- 在用户登录逻辑中,把用户姓名、角色直接写入ClaimsIdentity:
// 登录时添加自定义Claims示例 var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName), new Claim(ClaimTypes.Role, roleName) }; var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));
如果你用的是ASP.NET Core Identity自带的登录逻辑,可以实现IUserClaimsPrincipalFactory<AppUser>接口自定义Claims发放,不需要修改原有登录流程。
2. 局部视图里直接从User对象读取信息,完全不需要查数据库:
@inject SignInManager<AppUser> SignInManager @if (SignInManager.IsSignedIn(User)) { // 直接从Claims读取,无数据库开销 string name = User.Identity.Name; string role = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Role)?.Value; // 后续渲染逻辑不变 }
方案2:用IMemoryCache缓存用户信息
如果不想修改Claims逻辑,用内存缓存也完全能满足需求,你没有分布式缓存的场景下足够使用:
- 在_LoginPartial.cshtml中注入
IMemoryCache - 以用户ID为缓存键,缓存用户姓名和角色,设置合理的过期时间(比如15分钟滑动过期):
@inject IMemoryCache _memoryCache @inject SignInManager<AppUser> SignInManager @inject UserManager<AppUser> UserManager @if (SignInManager.IsSignedIn(User)) { string userId = UserManager.GetUserId(User); // 从缓存读,没有则查询数据库并存入缓存 var (name, role) = await _memoryCache.GetOrCreateAsync($"UserInfo_{userId}", async entry => { entry.SlidingExpiration = TimeSpan.FromMinutes(15); // 合并为一次数据库查询,减少往返 var userData = await UserManager.Users .Where(u => u.Id == userId) .Select(u => new { FullName = u.AppUser.FirstName + " " + u.AppUser.LastName, u }) .FirstOrDefaultAsync(); string role = (await UserManager.GetRolesAsync(userData.u)).FirstOrDefault(); return (userData.FullName, role); }); // 后续渲染逻辑 }
注意:当用户修改个人信息、变更角色时,需要主动删除对应用户ID的缓存,避免展示旧数据。
导航栏链接的缓存优化
因为同角色的导航链接是完全一致的,这部分可以单独按角色缓存,进一步减少渲染开销:
<!-- 姓名单独渲染,不参与缓存 --> <span class="user-name">@name</span> <!-- 链接部分按角色缓存,所有同角色用户共用一份缓存 --> <cache vary-by="@role"> <ul class="navbar-nav mr-auto mt-2 mt-lg-0 d-lg-flex justify-content-center flex-fill "> <li class="nav-item "> <a class="nav-link text-dark text-uppercase font-size-xlarge font-weight-light" asp-area="" asp-controller="Home" asp-action="Index">Home</a> </li> @switch (role) { case "Student": <li class="nav-item"> <a class="nav-link" asp-area="Exams" asp-controller="Schedules" asp-action="Index">Exams</a> </li> break; case "Instructor": <li class="nav-item"> <a class="nav-link" asp-area="Programs" asp-controller="Programs" asp-action="Index">Programs</a> </li> break; case "Administrator": break; } </ul> </cache>
内容的提问来源于stack exchange,提问作者Amal K
相关产品推荐
相关产品推荐

