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

如何在所有C#服务中复用多租户数据库切换代码?

多租户数据库名称替换的代码复用方案

方案1:抽象基类封装逻辑

把修改连接字符串的通用逻辑抽成抽象基类,所有需要该逻辑的服务都继承这个基类,彻底避免重复代码。

public abstract class BaseTenantService
{
    protected readonly erp_colombiaContext _context;

    protected BaseTenantService(erp_colombiaContext context, IHttpContextAccessor httpContextAccessor)
    {
        _context = context;
        var user = httpContextAccessor.HttpContext?.User;
        if (user?.Claims.Any() == true)
        {
            var companyName = user.Claims.FirstOrDefault(x => x.Type == "CompanyName")?.Value;
            if (!string.IsNullOrEmpty(companyName))
            {
                var connection = _context.Database.GetDbConnection();
                connection.ConnectionString = connection.ConnectionString.Replace("erp_colombia", companyName);
            }
        }
    }
}

业务服务只需继承基类,专注自身业务逻辑:

public class MyService : BaseTenantService, IMyService
{
    public MyService(erp_colombiaContext context, IHttpContextAccessor httpContextAccessor)
        : base(context, httpContextAccessor)
    {
        // 这里写当前服务的特有逻辑
    }
}

方案2:扩展方法封装逻辑

把修改连接字符串的逻辑封装成DbContext的扩展方法,每个服务里直接调用即可,代码更轻量化。

public static class TenantDbContextExtensions
{
    public static void SwitchToTenantDatabase(this erp_colombiaContext context, IHttpContextAccessor httpContextAccessor)
    {
        var user = httpContextAccessor.HttpContext?.User;
        if (user?.Claims.Any() == true)
        {
            var companyName = user.Claims.FirstOrDefault(x => x.Type == "CompanyName")?.Value;
            if (!string.IsNullOrEmpty(companyName))
            {
                var connection = context.Database.GetDbConnection();
                connection.ConnectionString = connection.ConnectionString.Replace("erp_colombia", companyName);
            }
        }
    }
}

业务服务中调用扩展方法:

public class MyService : IMyService
{
    private readonly erp_colombiaContext _context;

    public MyService(erp_colombiaContext context, IHttpContextAccessor httpContextAccessor)
    {
        _context = context;
        _context.SwitchToTenantDatabase(httpContextAccessor);
    }
}

方案3:EF Core拦截器(全局生效,推荐)

利用EF Core的DbConnectionInterceptor,在数据库连接打开前自动替换数据库名称,无需在每个服务中编写任何代码,全局统一处理。

public class TenantConnectionInterceptor : DbConnectionInterceptor
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public TenantConnectionInterceptor(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public override InterceptionResult ConnectionOpening(DbConnection connection, ConnectionEventData eventData, InterceptionResult result)
    {
        ModifyConnectionString(connection);
        return base.ConnectionOpening(connection, eventData, result);
    }

    public override Task<InterceptionResult> ConnectionOpeningAsync(DbConnection connection, ConnectionEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default)
    {
        ModifyConnectionString(connection);
        return base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
    }

    private void ModifyConnectionString(DbConnection connection)
    {
        var user = _httpContextAccessor.HttpContext?.User;
        if (user?.Claims.Any() == true)
        {
            var companyName = user.Claims.FirstOrDefault(x => x.Type == "CompanyName")?.Value;
            if (!string.IsNullOrEmpty(companyName) && connection.ConnectionString.Contains("erp_colombia"))
            {
                connection.ConnectionString = connection.ConnectionString.Replace("erp_colombia", companyName);
            }
        }
    }
}

在Program.cs中注册拦截器:

builder.Services.AddDbContext<erp_colombiaContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
    options.AddInterceptors(sp => sp.GetRequiredService<TenantConnectionInterceptor>());
});

builder.Services.AddScoped<TenantConnectionInterceptor>();
builder.Services.AddHttpContextAccessor();

这种方案一劳永逸,所有使用erp_colombiaContext的地方都会自动切换到租户数据库,无需在服务层做任何额外处理。


内容的提问来源于stack exchange,提问作者J.C

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 05:50:27