entity-frameworkasp.net-corejwtentity-framework-plus

AspNetCore 2.1 IdentityDbContext: How to get current username


In the Project I´m currently working at, I need to audit the changes to many database tables. To do so I´m considering working with the nuget Z.EntityFramework.Plus.Audit.EFCore. I´ve been experimenting and it seems to mostly fit my needs. I´ve been following the docs here.

It is possible to save audit entries in my controller like so:

public myFunction(){
   //... 
   var audit = new Audit();
   _context.SaveChanges(audit); 
}

But if I do it in such manner, it would be breaking the DRY principle. There is a way to override the SaveChanges() and SaveChagesAsync() functions, and it works wonders. Such thing is done like this:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
            AuditManager.DefaultConfiguration.AutoSavePreAction = (context, audit) =>
               // ADD "Where(x => x.AuditEntryID == 0)" to allow multiple SaveChanges with same Audit
               (context as ApplicationDbContext).AuditEntries.AddRange(audit.Entries);
        }


        public override int SaveChanges()
        {
            var audit = new Audit { }; //Problem would be here
            audit.PreSaveChanges(this);
            var rowAffecteds = base.SaveChanges();
            audit.PostSaveChanges();

            if (audit.Configuration.AutoSavePreAction != null)
            {
                audit.Configuration.AutoSavePreAction(this, audit);
                base.SaveChanges();
            }

            return rowAffecteds;
        }

        public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            var audit = new Audit { }; //Problem would be here
            audit.PreSaveChanges(this);
            var rowAffecteds = await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            audit.PostSaveChanges();

            if (audit.Configuration.AutoSavePreAction != null)
            {
                audit.Configuration.AutoSavePreAction(this, audit);
                await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }

            return rowAffecteds;
        }

//... DbContext Code
        }

Now that is a way better option, very DRY and more mantainable. Problem is, how to get the current username so that i can do something like

var audit = new Audit { CreatedBy = "my_current_username"};

In the controller you can use the instruction

var username = this.User.Identity.Name;

but this is not available in the IdentityDbContext. I did try injecting UserManager, but it didn´t have a function that might work.

tl;dr; How do I get the current UserName in IdentityDbContext when I´m using JWT tokens?


Solution

  • In ConfigureServices:

    services.AddHttpContextAccessor();
    
    services.AddDbContext<FooContext>((provider, options) =>
        options.UseSqlServer(Configuration.GetConnectionString("Foo"))
               .UseInternalServiceProvider(provider);
    

    In FooContext.SaveChanges/FooContext.SaveChangesAsync:

    var httpContextAccessor = this.GetService<IHttpContextAccesor>();
    var userName = httpContextAccessor.HttpContext?.User.Identity.Name;
    if (userName != null)
    {
        // You've got the username. Do something with it.
    }