.net Core Quartz Dependency Injection

后端 未结 4 2006
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-03 07:34

How can I configure Quartz in .net core to use dependency injection? I using standard .net core Dependency mechanism. In constructor of class that implemen

相关标签:
4条回答
  • 2020-12-03 08:00

    You can use the Quartz.Spi.IJobFactory interface and implement it. The Quartz documentations states:

    When a trigger fires, the Job it is associated to is instantiated via the JobFactory configured on the Scheduler. The default JobFactory simply activates a new instance of the job class. You may want to create your own implementation of JobFactory to accomplish things such as having your application’s IoC or DI container produce/initialize the job instance. See the IJobFactory interface, and the associated Scheduler.SetJobFactory(fact) method.

    ISchedulerFactory schedulerFactory = new StdSchedulerFactory(properties);
    var scheduler = schedulerFactory.GetScheduler();
    
    scheduler.JobFactory = jobFactory;
    

    Edit

    The implementation can look like this:

    public class JobFactory : IJobFactory
    {
        protected readonly IServiceProvider Container;
    
        public JobFactory(IServiceProvider container)
        {
            Container = container;
        }
    
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            return Container.GetService(bundle.JobDetail.JobType) as IJob;
        }
    
        public void ReturnJob(IJob job)
        {
            // i couldn't find a way to release services with your preferred DI, 
            // its up to you to google such things
        }
    }
    

    To use it with the Microsoft.Extensions.DependencyInjection create your container like this:

    var services = new ServiceCollection();
    services.AddTransient<IAuthorizable, AuthorizeService>();
    var container = services.BuildServiceProvider();
    var jobFactory = new JobFactory(container);
    

    References

    1. Quartz documentation

    2. Api

    0 讨论(0)
  • 2020-12-03 08:03

    Inspired by Rabbans great answer I created a complete implementation of a JobFactory for Microsoft.Extensions.DependencyInjection:

    Implementation

    using Microsoft.Extensions.DependencyInjection;
    using Quartz;
    using Quartz.Spi;
    using System;
    using System.Collections.Concurrent;
    
    class JobFactory : IJobFactory
    {
        protected readonly IServiceProvider _serviceProvider;
    
        protected readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new ConcurrentDictionary<IJob, IServiceScope>();
    
        public JobFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            var scope = _serviceProvider.CreateScope();
            IJob job;
    
            try
            {
                job = scope.ServiceProvider.GetRequiredService(bundle.JobDetail.JobType) as IJob;
            }
            catch
            {
                // Failed to create the job -> ensure scope gets disposed
                scope.Dispose();
                throw;
            }
    
            // Add scope to dictionary so we can dispose it once the job finishes
            if (!_scopes.TryAdd(job, scope))
            {
                // Failed to track DI scope -> ensure scope gets disposed
                scope.Dispose();
                throw new Exception("Failed to track DI scope");
            }
    
            return job;
        }
    
        public void ReturnJob(IJob job)
        {
            if (_scopes.TryRemove(job, out var scope))
            {
                // The Dispose() method ends the scope lifetime.
                // Once Dispose is called, any scoped services that have been resolved from ServiceProvider will be disposed.
                scope.Dispose();
            }
        }
    }
    

    Usage

    // Prepare the DI container
    var services = new ServiceCollection();
    // Register job
    services.AddTransient<MyJob>();
    // Register job dependencies
    services.AddTransient<IFoo, Foo>();
    var container = services.BuildServiceProvider();
    
    // Create an instance of the job factory
    var jobFactory = new JobFactory(container);
    
    // Create a Quartz.NET scheduler
    var schedulerFactory = new StdSchedulerFactory(properties);
    var scheduler = schedulerFactory.GetScheduler();
    
    // Tell the scheduler to use the custom job factory
    scheduler.JobFactory = jobFactory;
    

    The implementation has been tested in a .NET Core 2.1 console application with a single job and worked fine. Feel free to leave your feedback or improvement suggestions...

    0 讨论(0)
  • 2020-12-03 08:10

    No idea if this will be helpful or not but I created my own DI extension for Quartz that you are more then welcome to try: https://github.com/JaronrH/Quartz.DependencyInjection

    Short version is that you would use the AddQuartz() method to pass in the [optional] NaveValueCollection config and [required] Scrutor assembly searching you want (see https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/). For example:

    services.AddQuartz(s => s.FromAssemblyOf<Program>())
    

    This call will:

    • Find and automatically register all IJob, IAddScheduledJob, IAddSchedulerListener, IAddTriggerListener, and IAddJobListener implementations found in the assemblies (Scrutor). So, yes, you can use DI in your IJob classes this way!
    • Setup a Singleton IScheduler in DI that uses the above DI resources.
    • Register an adapter to the IScheduler so that Microsoft's Logging is used for Quartz's logging.

    You can then either use provider.StartQuartz() to start the Scheduler (which automatically looks for IApplicationLifetime and registers the Scheduler for Shutdown if available) or use conventional DI to get and start the services (provider.GetService().Start();).

    Hope this helps!

    0 讨论(0)
  • 2020-12-03 08:15

    I know it's an old question, but just wanted to add a 2020 answer:

    https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/microsoft-di-integration.html

    https://www.quartz-scheduler.net/documentation/quartz-3.x/packages/aspnet-core-integration.html

    I find it even easier than the approach that is not using the .NET Core DI. In the project I had to integrate it however, Autofac was using together with MS DI (can't tell you why) and it was complaining about some dependencies, so I had to add the following mapping as well:

    services.AddSingleton<ITypeLoadHelper, SimpleTypeLoadHelper>();
    

    The total solution for me looks like this:

    services.AddTransient<UpcomingReleasesNotificationJob>();
    services.AddSingleton<ITypeLoadHelper, SimpleTypeLoadHelper>();
    
    var jobKey = new JobKey("notificationJob");
    services.AddQuartz(q =>
    {
       q.SchedulerId = "JobScheduler";
       q.SchedulerName = "Job Scheduler";
       q.UseMicrosoftDependencyInjectionScopedJobFactory();
       q.AddJob<UpcomingReleasesNotificationJob>(j => j.WithIdentity(jobKey));
       q.AddTrigger(t => t
          .WithIdentity("notificationJobTrigger")
          .ForJob(jobKey)
          .StartNow()
          .WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(14, 00))
       );
    });
    
    services.AddQuartzServer(options =>
    {
       options.WaitForJobsToComplete = true;
    });
    
    0 讨论(0)
提交回复
热议问题