Search code examples
c#asp.net-coreentity-framework-core.net-core

How to get user information in DbContext using Net Core


I am trying to develop a class library in which i want to implement custom DbContext. In the SaveChanges method of the DbContext, i need to get current user’s information(department, username etc.) for auditing purpose. Some part of the DbContext code is below:

public override int SaveChanges()
{
    // find all changed entities which is ICreateAuditedEntity 
    var addedAuditedEntities = ChangeTracker.Entries<ICreateAuditedEntity>()
           .Where(p => p.State == EntityState.Added)
           .Select(p => p.Entity);

    var now = DateTime.Now;

    foreach (var added in addedAuditedEntities)
    {
        added.CreatedAt = now;
        added.CreatedBy = ?;
        added.CreatedByDepartment = ?
    }
    return base.SaveChanges();
}

Two options coming to mind:

  • Using HttpContext.Items to keep user information, injecting IHttpContextAccessor and getting information from the HttpContext.Items(In this case DbContext depends HttpContext, is it correct?)
  • Using ThreadStatic object instead of HttpContext.Items and getting information from the object( I read some posts that ThreadStatic is not safe)

Question : Which is the best fit into my case? Is there another way you suggest?


Solution

  • I implemented an approach similar to this that is covered in this blog post and basically involves creating a service that will use dependency injection to inject the HttpContext (and underlying user information) into a particular context, or however you would prefer to use it.

    A very basic implementation might look something like this:

    public class UserResolverService  
    {
        private readonly IHttpContextAccessor _context;
        public UserResolverService(IHttpContextAccessor context)
        {
            _context = context;
        }
    
        public string GetUser()
        {
           return _context.HttpContext.User?.Identity?.Name;
        }
    }
    

    You would just need to inject this into the pipeline within the ConfigureServices method in your Startup.cs file :

    services.AddTransient<UserResolverService>();
    

    And then finally, just access it within the constructor of your specified DbContext :

    public partial class ExampleContext : IExampleContext
    {
        private YourContext _context;
        private string _user;
        public ExampleContext(YourContext context, UserResolverService userService)
        {
            _context = context;
            _user = userService.GetUser();
        }
    }
    

    Then you should be able to use _user to reference the current user within your context. This can easily be extended to store / access any content available within the current request as well.