English 中文(简体)
Quartz.Net and dependancy injection or calling a job from external dll at runtime [duplicate]
原标题:

Currently I am writing a service using Quartz.NET to schedule the running of it.

I was wondering if anyone has any experience of using constructor injection with Quartz.NET and Simple Injector.

Below is essentially what I wish to achieve

public class JobImplementation: IJob
{
    private readonly IInjectedClass injectedClass;

    public JobImplementation(IInjectedClass _injectedClass)
    {
         injectedClass = _injectedClass
    }

    public void Execute(IJobExecutionContext _context)
    {
        //Job code
    }
最佳回答

According to this blog post, you would need to implement a custom IJobFactory, like this:

public class SimpleInjectorJobFactory : IJobFactory
{
    private readonly Container container;
    private readonly Dictionary<Type, InstanceProducer> jobProducers;

    public SimpleInjectorJobFactory(
        Container container, params Assembly[] assemblies)
    {
        this.container = container;

        // By creating producers, jobs can be decorated.
        var transient = Lifestyle.Transient;
        this.jobProducers =
            container.GetTypesToRegister(typeof(IJob), assemblies).ToDictionary(
                type => type,
                type => transient.CreateProducer(typeof(IJob), type, container));
    }

    public IJob NewJob(TriggerFiredBundle bundle, IScheduler _)
    {
        var jobProducer = this.jobProducers[bundle.JobDetail.JobType];
        return new AsyncScopedJobDecorator(
            this.container, () => (IJob)jobProducer.GetInstance());
    }

    public void ReturnJob(IJob job)
    {
        // This will be handled automatically by Simple Injector
    }

    private sealed class AsyncScopedJobDecorator : IJob
    {
        private readonly Container container;
        private readonly Func<IJob> decorateeFactory;

        public AsyncScopedJobDecorator(
            Container container, Func<IJob> decorateeFactory)
        {
            this.container = container;
            this.decorateeFactory = decorateeFactory;
        }

        public async Task Execute(IJobExecutionContext context)
        {
            using (AsyncScopedLifestyle.BeginScope(this.container))
            {
                var job = this.decorateeFactory();
                await job.Execute(context);
            }
        }
    }
}

Furthermore, you ll need the following registrations:

var container = new Container();

container.Options.ScopedLifestyle = new AsyncScopedLifestyle();

var factory = new StdSchedulerFactory();

IScheduler scheduler = await factory.GetScheduler();

scheduler.JobFactory = new SimpleInjectorJobFactory(
    container, 
    Assembly.GetExecutingAssembly()); // assemblies that contain jobs

// Optional: register some decorators
container.RegisterDecorator(typeof(IJob), typeof(LoggingJobDecorator));

container.Verify();
问题回答

Late to the party, but https://github.com/hbiarge/Quartz.Unity works well for combining Quartz.NET and Unity.

IUnityContainer container = new UnityContainer();
container.AddNewExtension<Quartz.Unity.QuartzUnityExtension>();
// do your other Unity registrations
IScheduler scheduler = container.Resolve<IScheduler>();

scheduler.ScheduleJob(
    new JobDetailImpl(myCommandName, typeof(MyCommand)),
    TriggerBuilder.Create()
        .WithCronSchedule(myCronSchedule)
        .StartAt(startTime)
        .Build()
);
scheduler.Start();

There are few steps to use Quartz.net with dependency injection engine from asp.net core.

Add nuget package to your project:

Microsoft.Extensions.DependencyInjection

Create custom JobFactory:

public class JobFactory : IJobFactory
{
    protected readonly IServiceProvider _serviceProvider;

     public JobFactory(IServiceProvider serviceProvider) 
         => _serviceProvider = serviceProvider;

     public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
         => _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob;

     public void ReturnJob(IJob job) 
         => (job as IDisposable)?.Dispose();
 }

Specify JobFactory when configuring scheduler:

 var scheduler = await StdSchedulerFactory.GetDefaultScheduler();
 scheduler.JobFactory = new JobFactory(_serviceProvider);

For someone can be usefull example of win service with Quartz.net and DI (from asp.net core) on the board:

public class WinService : ServiceBase
{
    private Scheduler _scheduleManager;

    private readonly Startup _startup;

    public WinService()
    {
        ServiceName = "SomeWinService";
        _startup = new Startup();
    }

    static void Main(string[] args)
    {
        var service = new WinService();

        // Working as Windows-service
        if (Console.IsInputRedirected && Console.IsOutputRedirected)
        {
            ServiceBase.Run(service);
        }
        // Working as console app
        else
        {
            service.OnStart(args);
            Console.WriteLine("Press any key to stop...");
            Console.ReadKey();
            service.OnStop();
        }
    }
 
    protected override void OnStart(string[] args)
    {
        _startup.RegisterServices();
        _scheduleManager = new Scheduler(_startup.ServiceProvider);
        _scheduleManager.StartTracking().Wait();
    }

    protected override void OnPause()
        => _scheduleManager.PauseTracking().Wait();

    protected override void OnContinue()
        => _scheduleManager.ResumeTracking().Wait();
 
    protected override void OnStop()
    {
        _scheduleManager.StopTracking().Wait();
        _startup.DisposeServices();
    }
}

public class Startup
{
    private IServiceProvider _serviceProvider;

    public IServiceProvider ServiceProvider => _serviceProvider;

    public void RegisterServices()
    {        
        _serviceProvider = new ServiceCollection()
            //.AddTransient(...)
            //.AddScoped(...)
            //.AddSingleton(...)
            .BuildServiceProvider();

    }

    public void DisposeServices()
    {
        if (_serviceProvider == null)
            return;

        if (_serviceProvider is IDisposable)
        {
            ((IDisposable)_serviceProvider).Dispose();
        }
    }
}

public class Scheduler
{        
    private readonly IServiceProvider _serviceProvider;
   
    private IScheduler _scheduler;
   
    public Scheduler(IServiceProvider serviceProvider) 
        => _serviceProvider = serviceProvider;
   
    public async Task StartTracking()
    {
        _scheduler = await StdSchedulerFactory.GetDefaultScheduler();
        _scheduler.JobFactory = new JobFactory(_serviceProvider);
        await _scheduler.Start();
       
        // Schedule your jobs here
    }
  
    public async Task PauseTracking() => await _scheduler?.PauseAll();
   
    public async Task ResumeTracking() => await _scheduler?.ResumeAll();
  
    public async Task StopTracking() => await _scheduler?.Shutdown();
}




相关问题
Manually implementing high performance algorithms in .NET

As a learning experience I recently tried implementing Quicksort with 3 way partitioning in C#. Apart from needing to add an extra range check on the left/right variables before the recursive call, ...

Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

How do I compare two decimals to 10 decimal places?

I m using decimal type (.net), and I want to see if two numbers are equal. But I only want to be accurate to 10 decimal places. For example take these three numbers. I want them all to be equal. 0....

Exception practices when creating a SynchronizationContext?

I m creating an STA version of the SynchronizationContext for use in Windows Workflow 4.0. I m wondering what to do about exceptions when Post-ing callbacks. The SynchronizationContext can be used ...

Show running instance in single instance application

I am building an application with C#. I managed to turn this into a single instance application by checking if the same process is already running. Process[] pname = Process.GetProcessesByName("...

How to combine DataTrigger and EventTrigger?

NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger? I think I need to combine an EventTrigger and a DataTrigger to achieve what I m after: when ...

热门标签