需要一种方法来获取当前请求 URL 以在多租户应用程序中配置数据库上下文

Need a way to get the current request URL to configure the database context in multi-tenant application

我正在将 Web 应用程序从 asp.net mvc 迁移到 .net core (.net 5),这让我陷入困境。 该站点在 IIS 中配置为接受来自多个 URL 的请求,例如 site1.example.com 和 site2.example.com。每个站点都有自己的数据库,通过 entity framework 核心访问。

在旧的 .net 框架中,我能够使用 global.asax.cs 中的事件之一来解析传入请求 URL 并从配置文件中查找正确的租户数据库。我正在尝试在 asp.net core mvc.

中设置类似的东西

这是 startup.cs

中我的 ConfigureServices 方法的相关部分
public void ConfigureServices(IServiceCollection services)
{           
    services.AddHttpContextAccessor();
    services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
    services.AddDbContext<myDbContext>((serviceProvider, dbContextBuilder) =>
    {
        var tenantIdentifier = serviceProvider.GetRequiredService<ITenantIdentifier>();
        var connectionString = Configuration.GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
        dbContextBuilder.UseSqlServer(connectionString);
    }, ServiceLifetime.Scoped);

    //other services configured below...
}

然后租户标识符如下所示:

public interface ITenantIdentifier
{
    string GetCurrentTenantId();
}

public class UrlTenantIdentifier : ITenantIdentifier
{
    readonly IHttpContextAccessor _httpContextAccessor;
    readonly ILogger<UrlTenantIdentifier> _logger;

    public UrlTenantIdentifier(IHttpContextAccessor httpContextAccessor, ILogger<UrlTenantIdentifier> logger)
    {
        _httpContextAccessor = httpContextAccessor;
        _logger = logger;
    }

    public string GetCurrentTenantId()
    {
        //_httpContextAccessor is null here

        //logic below for parsing URL and finding if we're site1 or site2
    }
}

现在有我不知道的正确方法吗?当我直到运行时才知道连接字符串键时,如何为依赖注入设置 entity framework 数据库上下文?我会在 IIS 中配置单独的站点和虚拟目录吗?

重构 DbContext 以覆盖 OnConfiguring 成员。注入配置和上下文访问器并在那里执行配置。

public class myDbContext : DbContext {
    private readonly ITenantIdentifier tenantIdentifier;
    private readonly IConfiguration configuration;

    public myDbContext(IConfiguration configuration, ITenantIdentifier tenantIdentifier) {
        this.configuration = configuration;
        this.tenantIdentifier = tenantIdentifier;
    }
    
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
        var connectionString = configuration
            .GetConnectionString(tenantIdentifier.GetCurrentTenantId() + "myDataModel");
        optionsBuilder.UseSqlServer(connectionString);
    }
}

DbContext 正在 created/initialized 时尝试访问请求上下文在请求流中为时过早,无法访问所需信息。它需要在上下文已经初始化和注入之后发生。

public void ConfigureServices(IServiceCollection services)  
    services.AddHttpContextAccessor();
    services.AddSingleton<ITenantIdentifier, UrlTenantIdentifier>();
    services.AddDbContext<myDbContext>(); //Simplified since configuration is internal

    //other services configured below...
}

参考DbContext Lifetime, Configuration, and Initialization