.NET Core DbContext 动态连接字符串

8
我将尝试根据每个HTTP请求头设置我的DbContext的连接字符串。在.NET Core中是否可以这样做?我在MVC5中实现了它,但我无法在.NET Core中实现它。

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

我不知道HTTP头在哪里,那么我该去哪里找到它呢?


MVC没有连接字符串,与数据库无关。EF有关。您是否尝试像以前的EF版本那样设置连接字符串?您尝试了什么?请发布代码。 - Panagiotis Kanavos
顺便问一下,你所说的动态是什么意思?你想针对每个请求使用不同的数据库,为每个终端用户使用不同的凭据,还是不想使用连接字符串的配置文件?否则,为什么不直接使用存储在配置文件中的连接字符串呢? - Panagiotis Kanavos
我想要实现的是:我正在尝试根据每个HTTP请求连接到不同的数据库。在HTTP请求头中,我发送了connectionString的名称。 - tunoandsuno
2个回答

11

您应该能够像这样做,以便在DbContext类型实例化中使用HTTP请求内容:

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;
}

由于EFCore的AddDbContext<TDbContext>扩展方法已经将DbContextOptions注册为单例,因此我们需要覆盖此注册并添加自己的DbContextOption工厂方法,该方法使用HttpContext并在Scoped生命周期内执行。

这样,我们可以在每个请求中更改选项(包括连接字符串)。


我会尝试这个。我的目标是根据HTTP请求头连接到不同的connectionStrings。我将在HTTP请求头中发送连接字符串。 - tunoandsuno
仅适用于第一次加载。不适用于每个HTTP请求。我该如何实现这一点? - tunoandsuno
@tunoandsuno编辑了答案。请尝试使用此解决方案。 - Federico Dipuma
谢谢你帮助我,Federico。但是为什么代码从未进入函数 -> private DbContextOptions<MyDbContext> DbContextOptionsFactory(IServiceProvider provider)? - tunoandsuno
@tunoandsuno 很抱歉,看起来Entity Framework希望在服务集合中注册DbContextOptionsDbContextOptions<MyDbContext>两者。已经进行了编辑,现在应该可以正常工作。同时,请确保您的DbContext实现只有一个构造函数,并且该构造函数的参数为DbContextOptions<MyDbContext> - Federico Dipuma
显示剩余3条评论

7
这样你甚至可以更加简洁地完成它。
services.AddScoped<ISqlConnectionContext, SqlConnectionContext>();
services.AddDbContext<SqlDbContext>((sp, builder) =>
    builder.UseSqlServer(sp.GetRequiredService<ISqlConnectionContext>().GetConnectionString()));

SqlConnectionContext 现在可以按照你想要的任何方式实现,例如使用 IHttpContextAccessor


2
看起来在 .NET Core 3.1 中已经不存在了 services.Replace,而这个方法对我很有效。 - Yehor Androsov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接