Accessing the HttpContext from a DbContext

Sometimes it might be necessary to access the current HttpContext from inside a DbContext, namely, from inside the OnConfiguring or OnModelCreating methods. Why? Well, for once, because of multitenancy: we may want to be able to decide the connection string to use based on the requesting or the host's domain, the current user or some other request parameter. Here the internal dependency injection (Instance) can’t help, because it cannot be used inside these methods.

The only option we have is to inject the IHttpContextAccessor class through our DbContext class’ constructor, and, from it, get hold of the HttpContext.

First we need to register the IHttpContextAccessor service in ConfigureServices:

services.AddHttpContextAccessor();

We also need to register our context for dependency injection:

services.AddDbContext<MyContext>();

A context registered this way needs to have a special constructor that has a parameter of type DbContextOptions (or DbContextOptions<MyContext>), in our case, we just need to add an extra parameter of type IHttpContextAccessor:

public class MyContext : DbContext
{
    public MyContext(DbContextOptions options, IHttpContextAccessor httpContextAccessor) : base(options)
    {
        this.HttpContextAccessor = httpContextAccessor;
    }

    protected IHttpContextAccessor HttpContextAccessor { get; }
}

Finally, when you need to access the HttpContext, you just need to retrieve it from the IHttpContextAccessor:

protected internal override void OnConfiguring(DbContextOptionsBuilder builder)
{
    var httpContext = this.HttpContextAccessor.HttpContext;
    var tenantService = httpContext.RequestServices.GetService<ITenantService>();
    var connectionString = tenantService.GetConnectionString(httpContext);
   
    builder.UseSqlServer(connectionString); base.OnConfiguring(builder);
}

Here the hypothetical ITenantService provides a method GetConnectionString that returns a connection string for the current HttpContext, and we use it with the SQL Server provider.

Hope you find this useful! Winking smile

                             

8 Comments

  • This is not the only way to pass a context around to various components / layers of your system. I've had success with adding tenant data to the thread executing the code and pulling the tenant information from there. in fact with this method you're binding ASP.NET to Entity Framework. Not a good story for coupling.

  • @john Hinz: true, even though that was not my concern. In general I would avoid that kind of things because of the Law of Demeter.

  • @john Hinz: true, even though that was not my concern. In general I would avoid that kind of things because of the Law of Demeter.

  • @John Hinz: thanks for raising this, that does deserve another post! ;-)

  • To avoid unsafe code, never pass the HttpContext into a method that performs background work. Pass the required data instead. In the following example, SendEmailCore is called to start sending an email. The correlationId is passed to SendEmailCore, not the HttpContext.

  • @Evelien Janssens: so and so. In this case, my objective was just to show that the GetConnectionString could take something from the context, either the user or the client IP or the requested domain name. In real life, there wouldn't be any method like this, it would either take the user name or the client IP or the host domain name.

  • Hi Ricardo,

    Yours is an interesting idea but can cause issues when something like Task.Run is used and asked to do some background processing which will be executed after HttpRequest has already been completed
    Which will cause the NullRef exception while accessing HTTPContext.

    Just curious whether to overcome that should one use own service context or pass data explicitly.

  • @Gaurav Puri: but the DbContext is initialized with a scoped lifetime, which means it is disposed at the end of the request. I don't think I understood your objection, can you explain?

Add a Comment

As it will appear on the website

Not displayed

Your website