Search code examples
c#entity-frameworkasp.net-coreentity-framework-coreasp.net-identity

ASP.NET Core 2.2 get userId in DbContext


I am quite new to ASP.NET Core. I want to create ASP.NET Core 2.2 WebAPI, with Entity Framework Core and ASP.NET Identity. Also, I want to use JWT to secure access to that WebAPI with tokens.

I have successfully managed to insert user to DB and to get token back from my API, and token auth is working.

But, I also want to soft delete database records and to keep track on who has deleted/updated/added a record. Test table for that is Countries, where fields are only Title and Description.

So far, I have turned Internet upside-down, but no help :(

Las thing that I have tried is (userId is null):

  • In startup.cs:

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
  • in my AppDbContext.cs

    public class AppDbContext : IdentityDbContext<AppUser>{
    
        public AppDbContext(DbContextOptions options) : base(options)
        {
        }
    
        public DbSet<AppUser> AppUsers { get; set; }
    
        public DbSet<Country> Countries { get; set; } // just for testing
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
    
            // TODO - IGNORE THIS
            //builder.Entity<AppUser>().Property<bool>("IsDeleted");
            //builder.Entity<AppUser>().HasQueryFilter(m => EF.Property<bool>(m, "IsDeleted") == false);
        }
    
        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
        {
            CheckBeforeSaving();
            return (await base.SaveChangesAsync(true, cancellationToken).ConfigureAwait(false));
        }
    
        private void CheckBeforeSaving()
        {
            var httpContextAccessor = this.GetService<IHttpContextAccessor>();
            var userId = httpContextAccessor?.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
    
            foreach (var entry in ChangeTracker.Entries())
            {
                switch (entry.State)
                {
                    case EntityState.Detached:
                        break;
                    case EntityState.Unchanged:
                        break;
                    case EntityState.Deleted:
                        entry.State = EntityState.Modified;
                        entry.CurrentValues["IsDeleted"] = true;
                        entry.CurrentValues["DeletedBy"] = userId;
                        entry.CurrentValues["Deleted"] = DateTime.Now;
                        break;
                    case EntityState.Modified:
                        entry.CurrentValues["UpdatedBy"] = userId;
                        entry.CurrentValues["Updated"] = DateTime.Now;
                        break;
                    case EntityState.Added:
                        entry.CurrentValues["IsDeleted"] = false;
                        entry.CurrentValues["CreatedBy"] = userId;
                        entry.CurrentValues["Created"] = DateTime.Now;
                        entry.CurrentValues["Description"] = userId;
                        break;
                    default:
                        break;
                }
            }
        }}}
    

Solution

  • Try to inject IHttpContextAccessor into the ApplicationDbContext directly:

    public class ApplicationDbContext : IdentityDbContext<AppUser>
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor httpContextAccessor)
            : base(options)
        {
            _httpContextAccessor= httpContextAccessor;
        }
    
        //other methods
    
       private void CheckBeforeSaving()
       {        
         var userId = _httpContextAccessor?.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
         //...
       }
    }