UserManager.GetUserAsync without tracking changes?

asp.net asp.net-core entity-framework entity-framework-core

Question

I'm using the following function to check whether the person that wants to access some database record is an owner of this record:

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;
    }
}

It works, but as I've noticed: ApplicationUser is now added to ChangeTracker. What it means is I cannot call userManager.GetUserAsync later in code, because I get this error:

The instance of entity type 'ApplicationUser' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.

I usually use .AsNoTracking() while accessing database records, but there is nothing like that in userManager. How would you solve this?

I am using it in MVC Controller method as follows:

if (!await new AccessGuard().IsOwnerOrHaveRightsAsync(_userManager, Post.Author, User))
{
     return Unauthorized();
}
1
0
2/7/2019 2:24:30 PM

Accepted Answer

You cannot use .AsNoTracking() with await userManager.GetUserAsync(User);. Alternatively you can do as follows:

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;
   }
}

Then you should register IHttpContextAccessor in the Startup class as follows:

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

    // Or you can also register as follows

    services.AddHttpContextAccessor();
}

Then to access AccessGuard service in your MVC controller method, first register AccessGuard in Startup as follows:

services.AddScoped<AccessGuard>();

Then in your controller method:

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

    // Now call `accessGuardService` service method here

     return View();
}

You can also get AccessGuard service as follows:

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

and it requires namespace using Microsoft.Extensions.DependencyInjection;

1
2/7/2019 2:27:20 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow