Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.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?

like image 251
tunoandsuno Avatar asked May 17 '17 09:05

tunoandsuno


2 Answers

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.

like image 105
Federico Dipuma Avatar answered Oct 03 '22 00:10

Federico Dipuma


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.

like image 24
Martin Avatar answered Oct 03 '22 00:10

Martin