2011-09-12 5 views
6

È possibile registrare un processo al quarzo per utilizzare sempre la stessa istanza IJob immessa dal contenitore DI Unity? Ho un esempio singolo "Monitor" di una classe Monitor proveniente da Unity DI, wich ho registrato come:Quartz, Unity e .NET

container.RegisterType<IMonitor, Monitor>(new ContainerControlledLifetimeManager()) 

e la mia IJob implementazione si aspetta di avere tale istanza del monitor iniettato in esso:

class MyJob : IJob { 
... 
[Dependency] IMonitor monitor {get; set;} 
... 
void Execute() 
... 
} 

ma quando gli eventi al quarzo si attivano, l'implementazione IJob.Execute() viene chiamata prima che la dipendenza venga iniettata. Come dovrei farlo funzionare? Dovrei prendere in considerazione altri contenitori DI o Scheduler?

Grazie

risposta

5

quarzo saranno reinstanziare implementazione dell'interfaccia lavoro su ogni evento incendi. È recommended utilizzare IStatefulJob se si desidera mantenere lo stato tra le esecuzioni di lavoro:

casi IStatefulJob seguono regole leggermente diverse da regolari istanze IJob . La differenza principale è che il loro JobDataMap associato viene ri-persistuto dopo ogni esecuzione del lavoro, preservando così lo stato per l'esecuzione successiva. L'altra differenza è che ai job statici non è consentito l'esecuzione simultanea, il che significa che i nuovi trigger si verificano prima che il completamento del metodo IJob.Execute sia ritardato.

Da quarzo tutorial:

StatefulJob

Ora, alcune note supplementari su dati di stato di un lavoro (aka JobDataMap): un'istanza di lavoro può essere definito come "stateful" o "non -stateful". I lavori senza stato hanno la propria JobDataMap salvata nel momento in cui sono stati aggiunti allo scheduler. Ciò significa che qualsiasi modifica apportata al contenuto di della mappa dei dati del lavoro durante l'esecuzione del lavoro andrà persa, e non verrà visualizzata dal lavoro alla successiva esecuzione. Probabilmente hai un errore nello , un job stateful è esattamente l'opposto: la sua JobDataMap viene ri-archiviata dopo ogni esecuzione del lavoro. Un effetto collaterale di che crea un job stateful è che non può essere eseguito contemporaneamente. O in altre parole: se un lavoro è stato, e un trigger tenta di "sparare" il lavoro mentre è già in esecuzione, il trigger bloccherà (attendere) fino al completamento dell'esecuzione precedente.

"Contrassegna" un lavoro in base allo stato in cui ha implementato l'interfaccia StatefulJob anziché nell'interfaccia di lavoro.

Un'altra opzione per voi è quello di implementare il proprio JobFactory:

Job 'istanze'

Un ultimo punto su questo argomento che può o non può essere ovvio, ormai: Si può creare una singola classe di lavoro e memorizzare molte "istanze definizioni" all'interno dello scheduler creando più istanze di JobDetails - ciascuna con il proprio set di proprietà e JobDataMap - e aggiungendole tutte allo scheduler.

All'attivazione di un trigger, il Job a cui è associato viene istanziato tramite JobFactory configurato nello Scheduler. L'impostazione predefinita JobFactory chiama semplicemente newInstance() sulla classe del lavoro. È possibile creare la propria implementazione di JobFactory per eseguire operazioni quali con l'IoC o il contenitore DI della vostra applicazione produrre/inizializzare l'istanza di lavoro .

+0

Beh, io non sono interessato a trattenendo stessa istanza IJob, quello che voglio è avere la stessa IMonitor iniettato in ogni nuova istanza IJob ... – j040p3d20

+2

Conservare il riferimento al 'Monitor' in JobDataMap o implementare il proprio JobFactory. – Dmitry

+0

L'interfaccia 'StatefulJob' è stata deprecata ... si consiglia di utilizzare le annotazioni http://quartz-scheduler.org/documentation/quartz-2.x/new-in-quartz-2 – Jaider

2

È possibile farlo implementando il proprio JobFactory. Dovrete implementare l'interfaccia IJobFactory:

public interface IJobFactory 
{ 
    /// <summary> 
    /// Called by the scheduler at the time of the trigger firing, in order to 
    /// produce a <see cref="IJob" /> instance on which to call Execute. 
    /// </summary> 
    /// <remarks> 
    /// <p> 
    /// It should be extremely rare for this method to throw an exception - 
    /// basically only the the case where there is no way at all to instantiate 
    /// and prepare the Job for execution. When the exception is thrown, the 
    /// Scheduler will move all triggers associated with the Job into the 
    /// <see cref="TriggerState.Error" /> state, which will require human 
    /// intervention (e.g. an application restart after fixing whatever 
    /// configuration problem led to the issue wih instantiating the Job. 
    /// </p> 
    /// 
/// </remarks> 
    /// <param name="bundle"> 
    /// The TriggerFiredBundle from which the <see cref="JobDetail" /> 
    /// and other info relating to the trigger firing can be obtained. 
    /// </param> 
    /// <throws> SchedulerException if there is a problem instantiating the Job. </throws> 
    /// <returns> the newly instantiated Job 
    /// </returns> 
    IJob NewJob(TriggerFiredBundle bundle); 
} 

Poi, impostare la proprietà quartz.scheduler.jobFactory.type del scheduler per il tipo di vostra fabbrica di posti di lavoro.

Per riferimento, ecco la fabbrica di lavoro di default che utilizza quartz.net:

public class SimpleJobFactory : IJobFactory 
{ 
    private static readonly ILog Log = LogManager.GetLogger(typeof (SimpleJobFactory)); 

    /// <summary> 
    /// Called by the scheduler at the time of the trigger firing, in order to 
    /// produce a <see cref="IJob" /> instance on which to call Execute. 
    /// </summary> 
    /// <remarks> 
    /// It should be extremely rare for this method to throw an exception - 
    /// basically only the the case where there is no way at all to instantiate 
    /// and prepare the Job for execution. When the exception is thrown, the 
    /// Scheduler will move all triggers associated with the Job into the 
    /// <see cref="TriggerState.Error" /> state, which will require human 
    /// intervention (e.g. an application restart after fixing whatever 
    /// configuration problem led to the issue wih instantiating the Job. 
/// </remarks> 
    /// <param name="bundle">The TriggerFiredBundle from which the <see cref="JobDetail" /> 
    /// and other info relating to the trigger firing can be obtained.</param> 
    /// <returns>the newly instantiated Job</returns> 
    /// <throws> SchedulerException if there is a problem instantiating the Job. </throws> 
    public virtual IJob NewJob(TriggerFiredBundle bundle) 
    { 
     JobDetail jobDetail = bundle.JobDetail; 
     Type jobType = jobDetail.JobType; 
     try 
     { 
      if (Log.IsDebugEnabled) 
      { 
       Log.Debug(string.Format(CultureInfo.InvariantCulture, "Producing instance of Job '{0}', class={1}", jobDetail.FullName, jobType.FullName)); 
      } 

      return (IJob) ObjectUtils.InstantiateType(jobType); 
     } 
     catch (Exception e) 
     { 
      SchedulerException se = new SchedulerException(string.Format(CultureInfo.InvariantCulture, "Problem instantiating class '{0}'", jobDetail.JobType.FullName), e); 
      throw se; 
     } 
    } 
} 

La linea interessante è:

return (IJob) ObjectUtils.InstantiateType(jobType); 
5

Dai un'occhiata alla Quartz.Unity.

https://www.nuget.org/packages/Quartz.Unity/1.0.1

Doc è molto scarsa, ma sembra che tutto quello che dovete fare è aggiungere il pacchetto NuGet e la seguente riga alla configurazione del contenitore.

var container = new UnityContainer().AddNewExtension<Quartz.Unity.QuartzUnityExtension>(); 
0

Creare una CustomJobfactory che sovrascriva SimpleJobFactory e utilizzare spring per creare un'istanza delle classi di lavoro.

/// <summary> 
/// Custom Job Factory 
/// </summary> 
public class CustomJobFactory : SimpleJobFactory 
{ 
    /// <summary> 
    /// Application context 
    /// </summary> 
    private IApplicationContext context; 

    /// <summary> 
    /// Initializes a new instance of the <see cref="CustomJobFactory" /> class. 
    /// </summary> 
    public CustomJobFactory() 
    { 
     this.context = ContextRegistry.GetContext(); 
    } 

    /// <summary> 
    /// Creates a new job instance 
    /// </summary> 
    /// <param name="bundle">Trigger bundle</param> 
    /// <param name="scheduler">Job scheduler</param> 
    /// <returns></returns> 
    public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) 
    { 
     IJobDetail jobDetail = bundle.JobDetail; 
     Type jobType = jobDetail.JobType; 
     return this.context.GetObject(jobType.Name) as IJob; 
    } 

    /// <summary> 
    /// Return job 
    /// </summary> 
    /// <param name="job">Job instance</param> 
    public override void ReturnJob(IJob job) 
    { 
    } 
}