【发布时间】:2021-12-18 06:11:39
【问题描述】:
我在 ASP.Net Core 5 Web 应用程序中安装了 Hangfire。 我按照 Hangfire 网站上的 Getting Started 指南进行了初始安装和配置,而且效果很好。
由于它是一个多租户应用程序(每个租户一个数据库),我需要能够在服务器上处理作业时连接到正确的数据库。
我遇到了this well explained post,据我所知,这正是我所需要的,但我不知道最后一步是如何从作业参数(租户标识)中获取值到方法中Hangfire 正在执行。
我已经创建了客户端过滤器
#region Hangfire Job Filters
#region Client filters
public class HfClientTenantFilter : IClientFilter
{
private readonly IMultiTenant _multiTenant;
public HfClientTenantFilter(IMultiTenant multiTenant)
{
_multiTenant = multiTenant;
}
public async void OnCreating(CreatingContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
MdxTenantConfig tenantConfig = await _multiTenant.GetTenantConfig();
filterContext.SetJobParameter("TenantId", tenantConfig.Id);
}
public void OnCreated(CreatedContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
}
}
public class HfClientFilterProvider : IJobFilterProvider
{
private readonly IMultiTenant _multiTenant;
public HfClientFilterProvider(IMultiTenant multiTenant) {
_multiTenant = multiTenant;
}
public IEnumerable<JobFilter> GetFilters(Job job)
{
return new JobFilter[]
{
new JobFilter(new CaptureCultureAttribute(), JobFilterScope.Global, null),
new JobFilter(new HfClientTenantFilter(_multiTenant),JobFilterScope.Global, null)
};
}
}
#endregion Client filters
和服务器过滤器:
#region Server filters
public class HfServerTenantFilter : IServerFilter
{
private readonly IHfTenantProvider _hfTenantProvider;
public HfServerTenantFilter(IHfTenantProvider hfTenantProvider)
{
_hfTenantProvider = hfTenantProvider;
}
public void OnPerforming(PerformingContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
var tenantId = filterContext.GetJobParameter<string>("TenantId");
// need to get the tenantId passed to the method that calls the creation of the DbContext
_hfTenantProvider.HfSetTenant(tenantId);
}
public void OnPerformed(PerformedContext filterContext)
{
if (filterContext == null) throw new ArgumentNullException(nameof(filterContext));
}
}
public class HfServerFilterProvider : IJobFilterProvider
{
private readonly IHfTenantProvider _hfTenantProvider;
public HfServerFilterProvider(IHfTenantProvider hfTenantProvider)
{
_hfTenantProvider = hfTenantProvider;
}
public IEnumerable<JobFilter> GetFilters(Job job)
{
return new JobFilter[]
{
new JobFilter(new CaptureCultureAttribute(), JobFilterScope.Global, null),
new JobFilter(new HfServerTenantFilter(_hfTenantProvider), JobFilterScope.Global, null),
};
}
}
#endregion Server filters
#endregion Hangfire Job Filters
在启动中附加服务器过滤器
services.AddHangfireServer(opt => {
opt.FilterProvider = new HfServerFilterProvider(new HfTenantProvider());
});
在调试中执行时,我看到参数TenantID 在客户端过滤器中设置正确,并且服务器过滤器也正确检索。
我现在正在尝试使该值可用于正在执行但尚未成功的方法。
我尝试使用范围服务:
#region Scoped Tenant provider service
public interface IHfTenantProvider {
void HfSetTenant(string TenantCode);
string HfGetTenant();
}
public class HfTenantProvider: IHfTenantProvider
{
public string HfTenantCode;
public void HfSetTenant(string TenantCode)
{
HfTenantCode = TenantCode;
}
public string HfGetTenant()
{
return HfTenantCode;
}
}
#endregion Scoped Tenant provider service
在启动中:
services.AddScoped<IHfTenantProvider, HfTenantProvider>();
在服务器过滤器的 OnPerforming 方法中,我将 Scoped 服务中的值设置为从作业参数中检索到的值(完整代码见上文),正如我在调试中看到的那样。
_hfTenantProvider.HfSetTenant(tenantId);
这是正在安排的测试方法:
public bool HfTest()
{
try
{
BackgroundJobClient jobClient = GetJobClient();
jobClient.Enqueue<MdxMetaCRUD>(crud => crud.HfTest());
}
catch (Exception)
{
return false;
}
return true;
}
这是我从 Scoped 服务中检索值的方法本身,但是 null:
public async Task HfTest()
{
string tenant =_hfTenantProvider.HfGetTenant();
using (var _dbContext = _contextHelper.CreateContext(true, tenant))
{
Entity entity = new()
{
Name = "HFtest",
Description = tenant,
DefaultAuditTrackRetention = 1
};
await _dbContext.Entities.AddAsync(entity);
}
}
ContextHelper 返回一个新的 DbContext:
public ApplicationDbContext CreateContext(bool backgroundProcess, string backgroundTenant)
{
return new ApplicationDbContext(_multiTenant, backgroundProcess, backgroundTenant);
}
DbContext 有一个覆盖来检索租户的连接字符串(在用户上下文中工作正常)。如果 Hangfire 正在执行作业,我正在尝试将 TenantId 传递给 _backgroundTenant
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
try
{
MdxTenantConfig tenantConfig = Task.Run(() => _multiTenant.GetTenantConfig(_backgroundProcess, _backgroundTenant)).Result;
optionsBuilder.UseSqlServer(tenantConfig.ConnectionString);
base.OnConfiguring(optionsBuilder);
}
catch //(Exception)
{
throw;
}
}
我希望正在启动的作业(正在检索服务器过滤器)和正在执行的作业在同一范围内(请求),但似乎不是。
我对范围服务做错了什么还是我的方法有误?我看到一些文章提到了“工厂”,但这超出了我的知识范围(目前)。
【问题讨论】:
标签: c# dependency-injection asp.net-core-mvc multi-tenant hangfire