如何在 ASP.NET Core 7 MVC 多租户应用程序中使用基于 URL 的动态连接字符串处理 Hangfire?

问题描述 投票:0回答:1

我正在开发一个多租户 ASP.NET Core 7 MVC 应用程序,其中每个租户都有自己的数据库。连接字符串根据 URL 变化,每个 URL 对应不同的租户及其特定的数据库连接字符串。我需要集成 Hangfire 来处理后台作业,但我在配置 Hangfire 以使用基于租户的动态连接字符串方面面临挑战。此外,我需要处理 Hangfire 仪表板以显示特定于当前租户的数据。

这是我目前的设置: ASP.NET Core 7 MVC 多租户实施 不同租户的不同连接字符串,由 URL 决定 Hangfire 用于后台作业处理 我的问题: 我需要将 Hangfire 配置为动态地为每个租户使用正确的连接字符串。但是,我不确定如何在连接字符串根据 URL 变化的多租户环境中正确设置 Hangfire。我还需要配置 Hangfire 仪表板以仅显示当前租户的数据。

我尝试过的: 自定义中间件:我实现了自定义中间件来拦截请求并设置连接字符串。 动态 DbContext:尝试根据 HttpContext 中存储的连接字符串动态配置 DbContext。

示例代码: 租户中间件:

public class TenantResolutionMiddleware
{
    private readonly RequestDelegate _next;

    public TenantResolutionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context, ITenantService tenantService)
    {
        try
        {
            await tenantService.IdentifyTenant(context);
            var tenant = await tenantService.GetCurrentTenantAsync(context);
            if (tenant != null)
            {
                context.Items["ConnectionString"] = tenant.ConnectionString;
                context.Items["TenantConnectionString"] = tenant.ConnectionString;
                context.Items["TenantId"] = tenant.Id;
            }
            else
            {
                var defaultConnectionString = context.RequestServices.GetRequiredService<IConfiguration>().GetConnectionString("Web");
                context.Items["ConnectionString"] = defaultConnectionString;
                context.Items["TenantConnectionString"] = defaultConnectionString;
            }
        }
        catch (Exception ex)
        {
            context.Response.StatusCode = 500; // Internal Server Error
            await context.Response.WriteAsync("An error occurred while identifying the tenant.");
            return;
        }
        await _next(context);
    }
}

初始化租户数据库:

public static void InitializeTenantDatabases(this WebApplication app)
{
    using var scope = app.Services.CreateScope();
    var tenantService = scope.ServiceProvider.GetRequiredService<ITenantService>();

    var tenants = tenantService.GetAllTenants();

    foreach (var tenant in tenants)
    {
        using var tenantScope = app.Services.CreateScope();
        var dbContext = tenantScope.ServiceProvider.GetRequiredService<ApplicationDbContext>();

        dbContext.Database.SetConnectionString(tenant.ConnectionString);
        dbContext.Database.Migrate();

        var dataInitializers = tenantScope.ServiceProvider.GetServices<IDataInitializer>();
        foreach (var dataInitializer in dataInitializers)
            dataInitializer.InitializeData();
    }
}

ITenant服务接口:

public interface ITenantService
{
    Task IdentifyTenant(HttpContext context);
    Task<Entities.Tenant.Tenant> GetCurrentTenantAsync(HttpContext context);
    string GetCurrentTenantConnectionString();
    Guid GetCurrentTenantId();
    string GetCurrentTenantIdString();
    Entities.Tenant.Tenant GetCurrentTenant();
    Task<Entities.Tenant.Tenant> GetCurrentTenantAsyncCashing(HttpContext context);
    string GetConnectionStringByTenantId(Guid tenantId);
    string GetDatabaseProvider();
    IEnumerable<Entities.Tenant.Tenant> GetAllTenants();
}

我在寻找什么: 如何配置 Hangfire 以根据 URL 为每个租户动态使用正确的连接字符串。 如何处理 Hangfire 仪表板以显示特定于当前租户的数据。 有关如何在多租户 ASP.NET Core 7 MVC 应用程序中正确实现 Hangfire 的示例或资源。 在将 Hangfire 与多租户集成时我应该注意的任何潜在陷阱或最佳实践。

我尝试实现自定义中间件来根据 URL 设置连接字符串并将其与 Hangfire 集成。我希望 Hangfire 动态地为每个租户使用正确的连接字符串,并使用各自的数据库为每个租户执行后台作业。

预期结果:

Hangfire 会动态地从中间件获取连接字符串,并为正确的租户数据库执行作业。 Hangfire 仪表板将显示特定于当前租户的数据。 实际结果:

Hangfire未使用动态连接字符串,后台作业无法连接到正确的租户数据库。 Hangfire 仪表板没有显示特定于租户的数据。

asp.net-core entity-framework-core middleware multi-tenant hangfire
1个回答
0
投票

您可以使用几种不同的方法。第一个潜在问题是尝试对

static
使用
DbContexts
初始化。我建议为所有会话所依赖的身份验证/授权设置一个单独的有界
DbContext
,其中包括检索相关租户连接字符串。特定于租户的应用程序
DbContext
的范围应限于 Web 请求甚至更短,这意味着应使用适用于当前会话的租户对其进行初始化。

您可以考虑以下三种方法之一:

  1. 引入 SessionService 帮助器之类的东西来包装当前经过身份验证的会话状态以注入到 DbContext 中。这里,DbContext 初始化将从 IoC 容器注入的 ISessionService 获取连接字符串,该容器将返回特定于当前会话用户的连接字符串。

  2. 配置 IoC 容器以按需构建 DbContext。这将取决于您使用的 IoC 容器,我个人使用 Autofac 是因为它的可配置性。因此,而不是单个可配置的连接字符串:

    builder.RegisterType<AppDbContext>()
        .WithParameter(new NamedParameter("connectionString", connectionString))
        .AsSelf();
    

注册按需解决:

    builder.Register<AppDbContext>(context =>
    {   // However you want to fetch the connection string, from the container or a static Singleton, etc.
        ISessionService sessionService = context.Resolve<ISessionService>();
        var connectionString = sessionService.ConnectionString;
        // TODO: Validate and handle if the session times out or doesn't return a connection string.
        return new AppDbContext(connectionString);
    });
  1. 您可以注入一个
    DbContext
    ,而不是注入
    IDbContextFactory
    ,它包装当前会话检查连接字符串,并根据需要使用所需的连接字符串初始化
    DbContext
© www.soinside.com 2019 - 2024. All rights reserved.