You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 5 Web API 基于主DbContext动态修改从DbContext连接字符串方案咨询

实现方案说明

两种方案都可以实现需求,推荐优先用全局操作过滤器+作用域租户服务的方案,侵入性更低,不需要修改业务控制器代码。

方案1:全局操作过滤器(推荐)

步骤1:定义作用域级租户信息存储服务

用来在当前请求生命周期内传递查询到的租户数据库名

// 租户信息载体类
public class CurrentTenant
{
    /// <summary>
    /// 租户专属数据库名
    /// </summary>
    public string DatabaseName { get; set; }
}
// Program.cs中注册为作用域服务
builder.Services.AddScoped<CurrentTenant>();

步骤2:实现全局异步操作过滤器

在过滤器中完成租户校验、数据库名查询逻辑

public class TenantValidateFilter : IAsyncActionFilter
{
    private readonly DbContextMaster _masterDb;
    private readonly CurrentTenant _currentTenant;
    private readonly IConfiguration _config;

    // 构造注入依赖
    public TenantValidateFilter(DbContextMaster masterDb, CurrentTenant currentTenant, IConfiguration config)
    {
        _masterDb = masterDb;
        _currentTenant = currentTenant;
        _config = config;
    }

    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // 1. 从请求头获取租户标识(你已实现该逻辑,直接替换成你的获取方法即可)
        var tenantId = context.HttpContext.Request.Headers["X-Tenant-Id"].FirstOrDefault();
        if(string.IsNullOrEmpty(tenantId))
        {
            context.Result = new BadRequestObjectResult("缺少租户标识");
            return;
        }

        // 2. 主库查询租户对应的数据库名
        var tenantDbName = await _masterDb.Tenants
            .Where(t => t.TenantId == tenantId)
            .Select(t => t.DbName)
            .FirstOrDefaultAsync();
        if(string.IsNullOrEmpty(tenantDbName))
        {
            context.Result = new BadRequestObjectResult("无效的租户标识");
            return;
        }

        // 3. 存储数据库名到当前作用域租户服务
        _currentTenant.DatabaseName = tenantDbName;

        // 执行后续控制器逻辑
        await next();
    }
}

步骤3:注册全局过滤器与动态DbContextChild

在Program.cs中完成配置:

// 注册全局过滤器
builder.Services.AddControllers(options =>
{
    options.Filters.Add<TenantValidateFilter>();
});

// 注册动态连接串的DbContextChild
builder.Services.AddDbContext<DbContextChild>((serviceProvider, opt) =>
{
    // 从当前作用域获取租户信息
    var currentTenant = serviceProvider.GetRequiredService<CurrentTenant>();
    // 读取连接串模板
    var connTemplate = _config.GetConnectionString("DbContextChild");
    // 替换占位符生成最终连接串
    var finalConnStr = connTemplate.Replace("{dbName}", currentTenant.DatabaseName);
    // 配置数据库连接,这里以SqlServer为例,其他数据库修改对应扩展方法即可
    opt.UseSqlServer(finalConnStr);
});

方案2:基类控制器实现

如果你的业务场景中只有部分控制器需要租户校验,可以选择该方案:

  • 定义基类ControllerBase,重写OnActionExecutingAsync方法,将上述过滤器中的校验逻辑写入该方法
  • 所有需要租户校验的业务控制器继承该基类即可

注意事项

  • DbContextChild默认注册为Scoped生命周期,不要修改为Singleton,否则会出现租户串库问题
  • 如果存在不需要租户校验的接口(如登录、健康检查),可以自定义[SkipTenantValidate]特性,在过滤器中判断接口是否标记该特性,直接跳过校验逻辑即可

内容的提问来源于stack exchange,提问作者Marcoscdoni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 09:18:03