8

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?

3
  • MVC doesn't have connection strings. It has nothing to do with databases. EF does. Have you tried setting the connection string as you did with the previous version of EF? What did you try? Please post the code Commented May 17, 2017 at 10:54
  • BTW what do you mean dynamic? Do you want to target different databases per request, use different credentials per end user or don't you want to use a configuration file for the connection string? Otherwise, why not just use the connection string stored in the config file? Commented May 17, 2017 at 10:55
  • What I am trying to achieve is: I'm trying to connect to different databases depending on each HTTP request. On the HTTP request header I send the name of the connectionString. Commented May 17, 2017 at 11:37

2 Answers 2

11

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.

Sign up to request clarification or add additional context in comments.

8 Comments

I will try this. What I am trying to do is to connect to different connectionStrings depending on the HTTP request Header. I send the conenctionstring in the HTTP request header.
Only works for first load. Not for every http request. How can I achieve this?
@tunoandsuno Edited the answer. Try with this solution.
Thank you for helping me Federico. But the code never gets into the function -> private DbContextOptions<MyDbContext> DbContextOptionsFactory(IServiceProvider provider) why is that?
@tunoandsuno Sorry about that, it appears that Entity Framework wants both DbContextOptions and DbContextOptions<MyDbContext> to be registered in the Service Collection. Edited, now it should work. Also ensure that your DbContext implementation has a single constructor which has DbContextOptions<MyDbContext> as parameter.
|
7

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.

1 Comment

looks like services.Replace from accepted answer does not exist anymore in .net core 3.1, and this one worked for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.