tunoandsuno
tunoandsuno

Reputation: 99

.NET core DbContext dynamic connectionstring

I am trying to set the connectionstring of my DbContext depending on each http request header. Is it possible to do so in .NET Core? I did it in MVC5 but I am not able to implement it in .NET core.

At

public void ConfigureServices(IServiceCollection services)
{
    // ...
}

I don't know the http header, so where can I do it?

Upvotes: 8

Views: 7809

Answers (2)

Martin
Martin

Reputation: 1048

This way you can do it even more streamlined

services.AddScoped<ISqlConnectionContext, SqlConnectionContext>();
services.AddDbContext<SqlDbContext>((sp, builder) =>
    builder.UseSqlServer(sp.GetRequiredService<ISqlConnectionContext>().GetConnectionString()));

The SqlConnectionContext can now be implemented in any way you want. For example using the IHttpContextAccessor.

Upvotes: 7

Federico Dipuma
Federico Dipuma

Reputation: 18315

You should be able to do something like this to use the HTTP request content inside the DbContext type instantiation:

using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

public void ConfigureServices(IServiceCollection services)
{
    // ...

    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    services.AddScoped<HttpContext>(p => p.GetService<IHttpContextAccessor>()?.HttpContext);
    services.AddDbContext<MyDbContext>();

    var descriptor = new ServiceDescriptor(
        typeof(DbContextOptions<MyDbContext>),
        DbContextOptionsFactory,
        ServiceLifetime.Scoped);

    var descriptorNonGeneric = new ServiceDescriptor(
        typeof(DbContextOptions),
        typeof(DbContextOptions<MyDbContext>), 
        ServiceLifetime.Scoped);

    services.Replace(descriptor);
    services.Replace(descriptorNonGeneric);

    // ...
}

private DbContextOptions<MyDbContext> DbContextOptionsFactory(IServiceProvider provider)
{
    var httpContext = provider.GetService<HttpContext>();
    // here we have the complete HttpContext
    var myHeader = httpContext.Request.Headers["MyHeader"];
    var connectionString = GetConnectionStringFromHeader(myHeader);

    var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
    optionsBuilder.UseSqlServer(connectionString);

    return optionsBuilder.Options;
}

Because the AddDbContext<TDbContext> extension method of EFCore will already register a DbContextOptions as a singleton, we need to overwrite this registration and add our own DbContextOption factory method which uses HttpContext and is executed with Scoped lifetime.

This way we may change options (including the connection string) on every request.

Upvotes: 11

Related Questions