Hangfire - 多租户,ASP.NET Core - 解析正确的租户

Dek*_*kim 10 c# dependency-injection hangfire .net-core asp.net-core

我有一个需要使用 Hangfire 的 SaaS 项目。我们已经实施了识别租户的要求。

建筑学

  • 持久层
    • 每个租户都有自己的数据库
  • .NET 核心
    • 我们已经有一个服务TenantCurrentService可以从源列表中返回租户的 ID [主机名、查询字符串等]
    • 我们已经有一个DbContextFactoryfor Entity Framework,它为客户端返回一个带有正确连接字符串的数据库上下文
    • 我们目前正在使用 ASP.NET Core DI(如果有帮助,愿意改变)
  • 吊火
    • 使用单一存储(例如:Postgresql),无论租户数量如何
    • 在适当的 Container/ServiceCollection 中执行作业,以便我们检索正确的数据库、正确的设置等。

问题

我正在尝试将 TenantId 标记到作业,从TenantCurrentService(这是一个范围服务)检索。

当作业被执行时,我们需要TenantId从作业中检索并将其存储在 中HangfireContext,以便TenantCurrentService知道从 Hangfire 检索到的 TenantId。从那里,我们的应用程序层将能够从我们的数据库连接到正确的数据库DbContextFactory

当前状态

  • 目前,我们已经能够使用IClientFilter.
  • 如何从 IServerFilter(负责检索保存的作业参数)检索我当前的 ASP.NET Core DI ServiceScope,以便我可以调用 .GetRequiredService().IdentifyTenant(tenantId)

有没有关于这个问题的好文章/或者你们可以提供的任何提示?

jbl*_*jbl 8

首先,您需要能够TenantId在您的TenantCurrentService. 然后,您可以依赖过滤器:

客户端(您排队工作的地方)

public class ClientTenantFilter : IClientFilter
{
        public void OnCreating(CreatingContext filterContext)
        {
           if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));

            filterContext.SetJobParameter("TenantId", TenantCurrentService.TenantId);
        }
}
Run Code Online (Sandbox Code Playgroud)

和服务器端(作业出队的地方)。

public class ServerTenantFilter : IServerFilter
{
    public void OnPerforming(PerformingContext filterContext)
    {
      if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));

      var tenantId = filterContext.GetJobParameter<string>("TenantId");
      TenantCurrentService.TenantId = tenantId;
    }
}
Run Code Online (Sandbox Code Playgroud)

当您通过以下方式配置服务器时,可以声明服务器过滤器IJobFilterProvider

        var options = new BackgroundJobServerOptions
        {
            Queues = ...,
            FilterProvider = new ServerFilterProvider()
        };
        app.UseHangfireServer(storage, options, ...);
Run Code Online (Sandbox Code Playgroud)

其中 ServerFilterProvider 是:

public class ServerFilterProvider : IJobFilterProvider
{
    public IEnumerable<JobFilter> GetFilters(Job job)
    {
        return new JobFilter[]
                   {
                       new JobFilter(new CaptureCultureAttribute(), JobFilterScope.Global, null),
                       new JobFilter(new ServerTenantFilter (), JobFilterScope.Global,  null),
                   };
    }
}
Run Code Online (Sandbox Code Playgroud)

可以在实例化 BackgroundJobClient 时声明客户端过滤器

var client = new BackgroundJobClient(storage, new BackgroundJobFactory(new ClientFilterProvider());
Run Code Online (Sandbox Code Playgroud)

whereClientFilterProvider表现为ServerFilterProvider,提供客户端过滤器

一个困难可能是在过滤器中使用 TenantCurrentService。我想这应该可以通过在 FilterProviders 中注入工厂并将其链接到过滤器来实现。

我希望这将有所帮助。