首页 > 解决方案 > UserManager.GetUserAsync 不跟踪更改?

问题描述

我正在使用以下函数来检查想要访问某些数据库记录的人是否是该记录的所有者:

public class AccessGuard
{
    public async Task<bool> IsOwnerOrHaveRightsAsync(UserManager<ApplicationUser> userManager, ApplicationUser claimant, ClaimsPrincipal User)
    {
        ApplicationUser fullUser = await userManager.GetUserAsync(User);
        if (claimant.Id == fullUser.Id)
        {
            return true;
        }

        return false;
    }
}

它有效,但正如我所注意到的:ApplicationUser现在已添加到 ChangeTracker。这意味着我不能userManager.GetUserAsync稍后在代码中调用,因为我收到此错误:

无法跟踪实体类型“ApplicationUser”的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。

我通常.AsNoTracking()在访问数据库记录时使用,但在 userManager 中没有类似的东西。你会如何解决这个问题?

我在 MVC Controller 方法中使用它,如下所示:

if (!await new AccessGuard().IsOwnerOrHaveRightsAsync(_userManager, Post.Author, User))
{
     return Unauthorized();
}

标签: asp.netentity-frameworkasp.net-coreentity-framework-core

解决方案


你不能使用.AsNoTracking()with await userManager.GetUserAsync(User);。或者,您可以执行以下操作:

public class AccessGuard
{
    private readonly ApplicationDbContext _context;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public AccessGuard(ApplicationDbContext context, IHttpContextAccessor httpContextAccessor)
    {
       _httpContextAccessor = httpContextAccessor;
       _context = context;
    }

   public async Task<bool> IsOwnerOrHaveRightsAsync(UserManager<ApplicationUser> userManager, ApplicationUser claimant, ClaimsPrincipal User)
   {
        var loggedInUserId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
        ApplicationUser fullUser = _context.ApplicationUsers.AsNoTracking()
                                  .FirstOrDefaultAsync(au => au.Id == loggedInUserId);
       if (claimant.Id == fullUser.Id)
       {
           return true;
       }

       return false;
   }
}

然后你应该IHttpContextAccessorStartup类中注册如下:

public void ConfigureServices(IServiceCollection services)
{
    services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    // Or you can also register as follows

    services.AddHttpContextAccessor();
}

AccessGuard然后在你的 MVC 控制器方法中访问服务,首先AccessGuard在 Startup 中注册如下:

services.AddScoped<AccessGuard>();

然后在您的控制器方法中:

public  IActionResult Index()
{
     AccessGuard accessGuardService = (AccessGuard) HttpContext.RequestServices.GetService(typeof(AccessGuard));

    // Now call `accessGuardService` service method here

     return View();
}

您还可以获得AccessGuard以下服务:

AccessGuard accessGuardService = HttpContext.RequestServices.GetService<AccessGuard>();

它需要命名空间using Microsoft.Extensions.DependencyInjection;


推荐阅读