ASP Core 2.1对Quartz的依赖注入

时间:2018-09-19 08:12:35

标签: c# asp.net-core dependency-injection quartz.net

我正在尝试将服务注入到我的SendEmailJob类中。 我使用标准的ASP Core依赖项注入和Quartz库进行调度。 我正在尝试基于this answer构建解决方案。但是我仍然面临着注入问题。

我有这样的代码设置:

//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;
        }
    }
}

JobFacctory的实现:

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或在任何其他范围内的服务上失败。

您能解释一下如何正确注入吗?

2 个答案:

答案 0 :(得分:2)

问题是您将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();
    }
} 

答案 1 :(得分:0)

我不确定您是否能够解决您在评论中提到的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) {

        }
    }
}