Hangfire - 多租户,ASP.NET 核心 - 解析正确的租户
Hangfire - Multi tenant, ASP.NET Core - Resolving the correct tenant
我有一个 SaaS 项目需要使用 Hangfire。我们已经实施了识别租户的要求。
建筑
- 持久层
- 每个租户都有自己的数据库
- .NET 核心
- 我们已经有一项服务
TenantCurrentService
,其中 return 是租户的 ID,来自源列表 [主机名、查询字符串等]
- 我们已经有 Entity Framework 的
DbContextFactory
,其中 return 具有正确客户端连接字符串的数据库上下文
- 我们目前正在使用 ASP.NET Core DI(如果有帮助,愿意更改)
- Hangfire
- 无论租户数量如何,都使用单一存储(例如:Postgresql)
- 以适当的方式执行作业 Container/ServiceCollection,以便我们检索正确的数据库、正确的设置等
问题
我正在尝试将 TenantId 标记为作业,从 TenantCurrentService
(这是一个 Scoped 服务)检索。
当作业执行后,我们需要从作业中检索 TenantId
并将其存储在 HangfireContext
中,这样 TenantCurrentService
就知道从 Hangfire 检索到的 TenantId。从那里,我们的应用程序层将能够从我们的 DbContextFactory
连接到正确的数据库
当前状态
- 目前,我们已经能够使用
IClientFilter
. 存储从我们的服务中检索到的 tenantId
- 如何从 IServerFilter(负责检索保存的作业参数)中检索我当前的 ASP.NET 核心 DI ServiceScope,以便我可以调用 .GetRequiredService().IdentifyTenant(tenantId)
关于这件事有什么好的文章/或者你们可以提供的任何提示吗?
首先,您需要能够在 TenantCurrentService
中设置 TenantId
。
然后,您可以依靠过滤器:
客户端(您排队作业的地方)
public class ClientTenantFilter : IClientFilter
{
public void OnCreating(CreatingContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
filterContext.SetJobParameter("TenantId", TenantCurrentService.TenantId);
}
}
和服务器端(作业出队的地方)。
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;
}
}
当您通过 IJobFilterProvider
:
配置服务器时,可以声明服务器过滤器
var options = new BackgroundJobServerOptions
{
Queues = ...,
FilterProvider = new ServerFilterProvider()
};
app.UseHangfireServer(storage, options, ...);
其中 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),
};
}
}
可以在实例化 BackgroundJobClient 时声明客户端过滤器
var client = new BackgroundJobClient(storage, new BackgroundJobFactory(new ClientFilterProvider());
其中 ClientFilterProvider
的行为与 ServerFilterProvider
相同,提供客户端过滤器
一个困难可能是在过滤器中提供 TenantCurrentService。我想这应该可以通过在 FilterProviders 中注入工厂并将其链接到过滤器来实现。
希望对您有所帮助。
我有一个 SaaS 项目需要使用 Hangfire。我们已经实施了识别租户的要求。
建筑
- 持久层
- 每个租户都有自己的数据库
- .NET 核心
- 我们已经有一项服务
TenantCurrentService
,其中 return 是租户的 ID,来自源列表 [主机名、查询字符串等] - 我们已经有 Entity Framework 的
DbContextFactory
,其中 return 具有正确客户端连接字符串的数据库上下文 - 我们目前正在使用 ASP.NET Core DI(如果有帮助,愿意更改)
- 我们已经有一项服务
- Hangfire
- 无论租户数量如何,都使用单一存储(例如:Postgresql)
- 以适当的方式执行作业 Container/ServiceCollection,以便我们检索正确的数据库、正确的设置等
问题
我正在尝试将 TenantId 标记为作业,从 TenantCurrentService
(这是一个 Scoped 服务)检索。
当作业执行后,我们需要从作业中检索 TenantId
并将其存储在 HangfireContext
中,这样 TenantCurrentService
就知道从 Hangfire 检索到的 TenantId。从那里,我们的应用程序层将能够从我们的 DbContextFactory
当前状态
- 目前,我们已经能够使用
IClientFilter
. 存储从我们的服务中检索到的 tenantId
- 如何从 IServerFilter(负责检索保存的作业参数)中检索我当前的 ASP.NET 核心 DI ServiceScope,以便我可以调用 .GetRequiredService().IdentifyTenant(tenantId)
关于这件事有什么好的文章/或者你们可以提供的任何提示吗?
首先,您需要能够在 TenantCurrentService
中设置 TenantId
。
然后,您可以依靠过滤器:
客户端(您排队作业的地方)
public class ClientTenantFilter : IClientFilter
{
public void OnCreating(CreatingContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
filterContext.SetJobParameter("TenantId", TenantCurrentService.TenantId);
}
}
和服务器端(作业出队的地方)。
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;
}
}
当您通过 IJobFilterProvider
:
var options = new BackgroundJobServerOptions
{
Queues = ...,
FilterProvider = new ServerFilterProvider()
};
app.UseHangfireServer(storage, options, ...);
其中 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),
};
}
}
可以在实例化 BackgroundJobClient 时声明客户端过滤器
var client = new BackgroundJobClient(storage, new BackgroundJobFactory(new ClientFilterProvider());
其中 ClientFilterProvider
的行为与 ServerFilterProvider
相同,提供客户端过滤器
一个困难可能是在过滤器中提供 TenantCurrentService。我想这应该可以通过在 FilterProviders 中注入工厂并将其链接到过滤器来实现。
希望对您有所帮助。