Hej,
Dziś miałem okazję po raz kolejny użyć Quartz .NET. Dlatego też postanowiłem napisać nowego posta na temat wstrzykiwania zależności w job’ach.
Pewnie niesamowicie spłycając:
Quartz jest to system planowania zadań który umożliwia łatwe tworzenie własnych zadań o ustalonej porze.
Kiedy pierwszy raz usłyszałem o tej bibliotece byłem pełen obaw o to czy to rzeczywiście działa jak należy. Po instalacji i przetestowaniu pierwszych „Jobów” byłem już dobrych myśli. Postanowiliśmy użyć tej biblioteki w naszym projekcie i zastępowaliśmy nasze joby Quartzowymi. Prawie na samym początku okazało się, że może być problem z wstrzykiwaniem zależności w konstruktory tych jobów.
Znalazłem wtedy paczkę Ninject.Extensions.Quartz – nie ufajcie jej działało, ale po zaktualizowaniu w projekcie Ninject do nowszej wersji – przestało.
To co robi rozszerzenie to rejestracja modułu QuartzNinjectModule w Ninject. Można zrobić to samemu i przechowywać te klasy w projekcie.
Rejestracja modułu w Ninject:
1 |
kernel.Load(new QuartzNinjectModule()); |
Budowa modułu QuartzNinjectModule:
1 2 3 4 5 6 7 8 |
public class QuartzNinjectModule : NinjectModule { public override void Load() { { Bind<ISchedulerFactory>().To<NinjectSchedulerFactory>(); Bind<IScheduler>().ToMethod(c => c.Kernel.Get<ISchedulerFactory>().GetScheduler()).InSingletonScope(); } } |
W module korzystamy z fabryki planera którą musimy sobie sami stworzyć:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class NinjectSchedulerFactory : StdSchedulerFactory { private readonly NinjectJobFactory _ninjectJobFactory; public NinjectSchedulerFactory(NinjectJobFactory ninjectJobFactory) { _ninjectJobFactory = ninjectJobFactory; } protected override IScheduler Instantiate(QuartzSchedulerResources quartzSchedulerResources, QuartzScheduler quartzScheduler) { quartzScheduler.JobFactory = _ninjectJobFactory; var scheduler = base.Instantiate(quartzSchedulerResources, quartzScheduler); scheduler.Start(); return scheduler; } } |
I ostatnią klasą jaką musimy dodać do projektu to NinjectJobFactory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class NinjectJobFactory : IJobFactory { private static readonly ILog Log = LogManager.GetLogger(typeof(NinjectJobFactory)); private readonly IKernel _kernel; static NinjectJobFactory() { } public NinjectJobFactory(IKernel kernel) { _kernel = kernel; } public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) { IJobDetail jobDetail = bundle.JobDetail; Type jobType = jobDetail.JobType; try { Log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}"); return _kernel.Get(jobType) as IJob; } catch (Exception ex) { throw new SchedulerException($"Problem instantiating class '{jobType.FullName}'", ex); } } public void ReturnJob(IJob job) { } } |
Po dodaniu tych klas do projektu, nasz HelloJob mający w konstruktorze zależność będzie działać o ile poprawnie zainicjujemy Scheduler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[DisallowConcurrentExecution] public class BreakerJob : IJob { private readonly IUserDataDomain _userDataDomain; public BreakerJob(IUserDataDomain userDataDomain) { _userDataDomain = userDataDomain; } public void Execute(IJobExecutionContext context) { // DO SOMETHING } } |
Z reguły tworzę sobie klasę pomocniczą o nazwie JobScheduler którego konstruktor wygląda w ten sposób:
1 2 3 4 5 |
publicJobScheduler(IScheduler scheduler) { _scheduler = scheduler; RegisterJobs(); } |
Poza rejestracją jobów umożliwiam uruchomienie IScheduler:
1 2 3 4 |
public void Start() { _scheduler.Start(); } |
I ostatnią rzeczą jaką robię to uruchomienie schedulera – JobScheduler stworzonego w naszym IoC.
1 2 |
var jobScheduler = _kernel.Get(); jobScheduler.Start(); |