首页 > 解决方案 > 需要一种获取当前请求 URL 以在多租户应用程序中配置数据库上下文的方法

问题描述

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

在旧的 .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
    }
}

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

标签: c#asp.net-coredependency-injectionentity-framework-core.net-5

解决方案


重构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为时过早,无法访问所需的信息。它需要在上下文已经初始化和注入之后发生。

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

    //other services configured below...
}

参考DbContext 生命周期、配置和初始化


推荐阅读