C# Quartz.NET 3.0似乎在同一范围内启动所有作业
在定义了两个依赖于作用域服务(ScopedDataAccess)的作业后,我很难将Quartz 3.0.7与ASP.NET Core 2.2结合使用,该服务是数据库上下文的包装器:C# Quartz.NET 3.0似乎在同一范围内启动所有作业,c#,quartz.net,asp.net-core-2.2,C#,Quartz.net,Asp.net Core 2.2,在定义了两个依赖于作用域服务(ScopedDataAccess)的作业后,我很难将Quartz 3.0.7与ASP.NET Core 2.2结合使用,该服务是数据库上下文的包装器: services.AddScoped<IScopedDataAccess, ScopedDataAccess>(); services.AddDbContext<AggregatorContext>(opt => opt.UseSqlServer(configuration.GetCo
services.AddScoped<IScopedDataAccess, ScopedDataAccess>();
services.AddDbContext<AggregatorContext>(opt => opt.UseSqlServer(configuration.GetConnectionString("Default")));
有没有一种方法可以使用Quartz.NET为不同的作业获取不同的作用域 据我所知,这在Quartz中是不可能的,我也遇到了同样的问题,我找到的唯一解决方案是使用ServiceLocator并在作业中显式创建范围 我的结尾是这样的:
// Pseudo-Code
public class MyJob : IJob
{
private readonly IServiceLocator _serviceLocator;
public MyJob(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
}
public async Task Execute(JobExecutionContext context)
{
using(_serviceLocator.BeginScope())
{
var worker = _serviceLocator.GetService<MyWorker>();
await worker.DoWorkAsync();
}
}
}
我们在这个实现中主要使用SimpleInjector:
/// <summary>
/// SimpleInjector implementation of the service locator.
/// </summary>
public class ServiceLocator : IServiceLocator
{
#region member vars
/// <summary>
/// The SimpleInjector container.
/// </summary>
private readonly Container _container;
#endregion
#region constructors and destructors
public ServiceLocator(Container container)
{
_container = container;
}
#endregion
#region explicit interfaces
/// <inheritdoc />
public IDisposable BeginAsyncScope()
{
return AsyncScopedLifestyle.BeginScope(_container);
}
/// <inheritdoc />
public TService GetInstance<TService>()
where TService : class
{
return _container.GetInstance<TService>();
}
}
//
///服务定位器的SimpleInjector实现。
///
公共类服务定位器:IServiceLocator
{
#区域成员变量
///
///SimpleInjector容器。
///
私有只读容器_容器;
#端区
#区域构造函数和析构函数
公共服务定位器(容器)
{
_容器=容器;
}
#端区
#区域显式接口
///
公共IDisposable BeginAsyncScope()
{
返回AsyncScopedLifestyle.BeginScope(_容器);
}
///
公共TService GetInstance()
where-TService:class
{
返回_container.GetInstance();
}
}
如您所见,这只是一个简单的包装器,但有助于向消费者隐藏真正的DI框架。
我希望这有助于理解您所需的实现。如果理解正确,我会将作业作为作用域(
services.Add(jobs.Select(jobType=>newServiceDescriptor(jobType,jobType,ServiceLifetime.scoped));
)保留作业,并使用服务定位器创建单独的作用域。我使用内置DI,但不清楚如何实现ServiceLocator服务。我已经搜索并找到了基于HttpContext.ApplicationServices的定位服务。但是HttpContext在此上下文中不可用(它与任何请求都不相关)。一旦我了解了服务定位器,我将测试建议的解决方案。谢谢。@Alexei作业不再是作用域,因为如果您打开作用域并手动解析服务,则不需要它。是的,这很有意义。我将重试提供的实现并让您知道。谢谢。因为我没有使用SimpleInjector,所以我在Startup.cs中创建了一个静态引用,我使用它为每个作业创建一个新范围:QuartzScopedProvider=services.BuildServiceProvider()
,服务定位器将使用它创建新范围:\u serviceScope=Startup.QuartzScopedProvider.CreateScope()代码>。这有意义吗?不管怎么说,你的回答真的让我明白了事情是怎么回事。
public class QuartzJobFactory : IJobFactory
{
private readonly IServiceProvider _serviceProvider;
public QuartzJobFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var jobDetail = bundle.JobDetail;
var job = (IJob)_serviceProvider.GetService(jobDetail.JobType);
return job;
}
public void ReturnJob(IJob job) { }
}
// Pseudo-Code
public class MyJob : IJob
{
private readonly IServiceLocator _serviceLocator;
public MyJob(IServiceLocator serviceLocator)
{
_serviceLocator = serviceLocator;
}
public async Task Execute(JobExecutionContext context)
{
using(_serviceLocator.BeginScope())
{
var worker = _serviceLocator.GetService<MyWorker>();
await worker.DoWorkAsync();
}
}
}
/// <summary>
/// A simple service locator to hide the real IOC Container.
/// Lowers the anti-pattern of service locators a bit.
/// </summary>
public interface IServiceLocator
{
/// <summary>
/// Begins an new async scope.
/// The scope should be disposed explicitly.
/// </summary>
/// <returns></returns>
IDisposable BeginAsyncScope();
/// <summary>
/// Gets an instance of the given <typeparamref name="TService" />.
/// </summary>
/// <typeparam name="TService">Type of the requested service.</typeparam>
/// <returns>The requested service instance.</returns>
TService GetInstance<TService>() where TService : class;
}
/// <summary>
/// SimpleInjector implementation of the service locator.
/// </summary>
public class ServiceLocator : IServiceLocator
{
#region member vars
/// <summary>
/// The SimpleInjector container.
/// </summary>
private readonly Container _container;
#endregion
#region constructors and destructors
public ServiceLocator(Container container)
{
_container = container;
}
#endregion
#region explicit interfaces
/// <inheritdoc />
public IDisposable BeginAsyncScope()
{
return AsyncScopedLifestyle.BeginScope(_container);
}
/// <inheritdoc />
public TService GetInstance<TService>()
where TService : class
{
return _container.GetInstance<TService>();
}
}