ASP Core 2.1 对 Quartz 的依赖注入
ASP Core 2.1 Dependency Injection to Quartz
我正在尝试将服务注入我的 SendEmailJob class。
我使用标准 ASP 核心依赖注入和 Quartz 库进行调度。
我正在尝试基于 构建解决方案。但我仍然面临注入问题。
我有这样的代码设置:
//Startup.cs, ConfigureServices
ServiceAutoConfig.Configure(allServices);
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddTransient<IJobFactory, JobFactory>((provider) => new JobFactory(services.BuildServiceProvider()));
services.AddTransient<SendEmailJob>();
//Startup.cs, Configure
app.UseQuartz((quartz) => quartz.AddJob<SendEmailJob>("SendEmailJob", "Email", mailSettings.EmailSchedulerInterval));
SendEmailJob 的实现:
public class SendEmailJob : IJob
{
private readonly IMessageService _messageService;
private static bool IsBusy = false;
public SendEmailJob(IMessageService messageService)
{
_messageService = messageService;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
if (IsBusy)
return;
IsBusy = true;
//...
}
catch (Exception error)
{
}
finally
{
IsBusy = false;
}
}
}
JobFactory 的实现:
public class JobFactory : IJobFactory
{
protected readonly IServiceProvider _container;
public JobFactory(IServiceProvider container)
{
_container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var res = _container.GetService(bundle.JobDetail.JobType) as IJob;
return res;
}
catch (Exception ex)
{
//ERROR- Cannot resolve 'Quartz.Jobs.SendEmailJob' from root provider because it
// requires scoped service 'BLL.Base.UnitOfWork.Interfaces.IUnitOfWork'.
throw;
}
}
public void ReturnJob(IJob job)
{
(job as IDisposable)?.Dispose();
}
}
实施Quartz.cs
public class Quartz
{
private IScheduler _scheduler;
public static IScheduler Scheduler { get { return Instance._scheduler; } }
private static Quartz _instance = null;
public static Quartz Instance
{
get
{
if (_instance == null)
{
_instance = new Quartz();
}
return _instance;
}
}
private Quartz()
{
Init();
}
private async void Init()
{
_scheduler = await new StdSchedulerFactory().GetScheduler();
}
public IScheduler UseJobFactory(IJobFactory jobFactory)
{
Scheduler.JobFactory = jobFactory;
return Scheduler;
}
public async void AddJob<T>(string name, string group, int interval)
where T : IJob
{
IJobDetail job = JobBuilder.Create<T>()
.WithIdentity(name, group)
.Build();
ITrigger jobTrigger = TriggerBuilder.Create()
.WithIdentity(name + "Trigger", group)
.StartNow()
.WithSimpleSchedule(t => t.WithIntervalInSeconds(interval).RepeatForever()) // Mit wiederholung alle interval sekunden
.Build();
await Scheduler.ScheduleJob(job, jobTrigger);
}
public static async void Start()
{
await Scheduler.Start();
}
}
以及 UseQuartzExtension 的实现:
public static void UseQuartz(this IApplicationBuilder app, Action<Quartz> configuration)
{
var jobFactory = new JobFactory(app.ApplicationServices);
Quartz.Instance.UseJobFactory(jobFactory);
configuration.Invoke(Quartz.Instance);
Quartz.Start();
}
并且在将 IMessageService
注入 SendMailJob
时出错。
因为它需要 UnitOfWork
或在任何其他作用域服务上失败。
你能解释一下如何正确注入吗?
问题是您将 IUnitOfWork
注册为作用域,但在解析它时您目前没有任何作用域。在解决你的工作之前创建它:
public class JobFactory : IJobFactory, IDisposable
{
protected readonly IServiceScope _scope;
public JobFactory(IServiceProvider container)
{
_scope = container.CreateScope();
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var res = _scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
return res;
}
public void ReturnJob(IJob job)
{
(job as IDisposable)?.Dispose();
}
public void Dispose()
{
_scope.Dispose();
}
}
我不确定您是否能够解决您在评论中提到的已处置 DbContext 问题,但我正在开发一个即将发布的具有相同问题的 .NET 核心应用程序并提出了解决方案类似于 Alex Riabov 但在作业结束时使用并发字典来处理范围。结果是在实例化新作业时将新的 dbcontext 注入到我的作业中。
public class QuartzJobFactory : IJobFactory
{
protected readonly IServiceProvider serviceProvider;
private ConcurrentDictionary<IJob, IServiceScope> scopes = new ConcurrentDictionary<IJob, IServiceScope>();
public QuartzJobFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
// instantiation of new job
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try {
var scope = serviceProvider.CreateScope();
var job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
scopes.TryAdd(job, scope);
return job;
}
catch (Exception ex) {
throw;
}
}
// executes when job is complete
public void ReturnJob(IJob job)
{
try {
(job as IDisposable)?.Dispose();
if (scopes.TryRemove(job, out IServiceScope scope))
scope.Dispose();
}
catch (Exception ex) {
}
}
}
我正在尝试将服务注入我的 SendEmailJob class。
我使用标准 ASP 核心依赖注入和 Quartz 库进行调度。
我正在尝试基于
我有这样的代码设置:
//Startup.cs, ConfigureServices
ServiceAutoConfig.Configure(allServices);
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddTransient<IJobFactory, JobFactory>((provider) => new JobFactory(services.BuildServiceProvider()));
services.AddTransient<SendEmailJob>();
//Startup.cs, Configure
app.UseQuartz((quartz) => quartz.AddJob<SendEmailJob>("SendEmailJob", "Email", mailSettings.EmailSchedulerInterval));
SendEmailJob 的实现:
public class SendEmailJob : IJob
{
private readonly IMessageService _messageService;
private static bool IsBusy = false;
public SendEmailJob(IMessageService messageService)
{
_messageService = messageService;
}
public async Task Execute(IJobExecutionContext context)
{
try
{
if (IsBusy)
return;
IsBusy = true;
//...
}
catch (Exception error)
{
}
finally
{
IsBusy = false;
}
}
}
JobFactory 的实现:
public class JobFactory : IJobFactory
{
protected readonly IServiceProvider _container;
public JobFactory(IServiceProvider container)
{
_container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try
{
var res = _container.GetService(bundle.JobDetail.JobType) as IJob;
return res;
}
catch (Exception ex)
{
//ERROR- Cannot resolve 'Quartz.Jobs.SendEmailJob' from root provider because it
// requires scoped service 'BLL.Base.UnitOfWork.Interfaces.IUnitOfWork'.
throw;
}
}
public void ReturnJob(IJob job)
{
(job as IDisposable)?.Dispose();
}
}
实施Quartz.cs
public class Quartz
{
private IScheduler _scheduler;
public static IScheduler Scheduler { get { return Instance._scheduler; } }
private static Quartz _instance = null;
public static Quartz Instance
{
get
{
if (_instance == null)
{
_instance = new Quartz();
}
return _instance;
}
}
private Quartz()
{
Init();
}
private async void Init()
{
_scheduler = await new StdSchedulerFactory().GetScheduler();
}
public IScheduler UseJobFactory(IJobFactory jobFactory)
{
Scheduler.JobFactory = jobFactory;
return Scheduler;
}
public async void AddJob<T>(string name, string group, int interval)
where T : IJob
{
IJobDetail job = JobBuilder.Create<T>()
.WithIdentity(name, group)
.Build();
ITrigger jobTrigger = TriggerBuilder.Create()
.WithIdentity(name + "Trigger", group)
.StartNow()
.WithSimpleSchedule(t => t.WithIntervalInSeconds(interval).RepeatForever()) // Mit wiederholung alle interval sekunden
.Build();
await Scheduler.ScheduleJob(job, jobTrigger);
}
public static async void Start()
{
await Scheduler.Start();
}
}
以及 UseQuartzExtension 的实现:
public static void UseQuartz(this IApplicationBuilder app, Action<Quartz> configuration)
{
var jobFactory = new JobFactory(app.ApplicationServices);
Quartz.Instance.UseJobFactory(jobFactory);
configuration.Invoke(Quartz.Instance);
Quartz.Start();
}
并且在将 IMessageService
注入 SendMailJob
时出错。
因为它需要 UnitOfWork
或在任何其他作用域服务上失败。
你能解释一下如何正确注入吗?
问题是您将 IUnitOfWork
注册为作用域,但在解析它时您目前没有任何作用域。在解决你的工作之前创建它:
public class JobFactory : IJobFactory, IDisposable
{
protected readonly IServiceScope _scope;
public JobFactory(IServiceProvider container)
{
_scope = container.CreateScope();
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var res = _scope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
return res;
}
public void ReturnJob(IJob job)
{
(job as IDisposable)?.Dispose();
}
public void Dispose()
{
_scope.Dispose();
}
}
我不确定您是否能够解决您在评论中提到的已处置 DbContext 问题,但我正在开发一个即将发布的具有相同问题的 .NET 核心应用程序并提出了解决方案类似于 Alex Riabov 但在作业结束时使用并发字典来处理范围。结果是在实例化新作业时将新的 dbcontext 注入到我的作业中。
public class QuartzJobFactory : IJobFactory
{
protected readonly IServiceProvider serviceProvider;
private ConcurrentDictionary<IJob, IServiceScope> scopes = new ConcurrentDictionary<IJob, IServiceScope>();
public QuartzJobFactory(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
// instantiation of new job
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
try {
var scope = serviceProvider.CreateScope();
var job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
scopes.TryAdd(job, scope);
return job;
}
catch (Exception ex) {
throw;
}
}
// executes when job is complete
public void ReturnJob(IJob job)
{
try {
(job as IDisposable)?.Dispose();
if (scopes.TryRemove(job, out IServiceScope scope))
scope.Dispose();
}
catch (Exception ex) {
}
}
}