开发者

Quartz, Unity & .NET

Is it possible开发者_JAVA技巧 to register a quartz job to always use the same IJob instance injected by DI container Unity? I have a single instance "monitor" of a class Monitor coming from Unity DI, wich I registered as:

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

and my IJob implementation expects to have that monitor instance injected into it:

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

but when quartz events fire, the IJob.Execute() implementation is called before the dependency gets injected. How should I get this working? Should I consider other DI containers or Schedulers instead ?

Thanks


Quartz will reinstantiate job interface implementation on every fire event. It is recommended to use IStatefulJob if you want to preserve state between job executions:

IStatefulJob instances follow slightly different rules from regular IJob instances. The key difference is that their associated JobDataMap is re-persisted after every execution of the job, thus preserving state for the next execution. The other difference is that stateful jobs are not allowed to Execute concurrently, which means new triggers that occur before the completion of the IJob.Execute method will be delayed.

From Quartz tutorial:

StatefulJob

Now, some additional notes about a job's state data (aka JobDataMap): A Job instance can be defined as "stateful" or "non-stateful". Non-stateful jobs only have their JobDataMap stored at the time they are added to the scheduler. This means that any changes made to the contents of the job data map during execution of the job will be lost, and will not seen by the job the next time it executes. You have probably guessed, a stateful job is just the opposite - its JobDataMap is re-stored after every execution of the job. One side-effect of making a job stateful is that it cannot be executed concurrently. Or in other words: if a job is stateful, and a trigger attempts to 'fire' the job while it is already executing, the trigger will block (wait) until the previous execution completes.

You 'mark' a Job as stateful by having it implement the StatefulJob interface, rather than the Job interface.

Another option for you is to implement your own JobFactory:

Job 'Instances'

One final point on this topic that may or may not be obvious by now: You can create a single job class, and store many 'instance definitions' of it within the scheduler by creating multiple instances of JobDetails - each with its own set of properties and JobDataMap - and adding them all to the scheduler.

When a trigger fires, the Job it is associated to is instantiated via the JobFactory configured on the Scheduler. The default JobFactory simply calls newInstance() on 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.


Have a look at Quartz.Unity.

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

Doc is very sparse, but it appears that all you need to do is add the nuget package and the following line to your container configuration.

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


You can do this by implementing your own JobFactory. You'll have to implement the IJobFactory interface:

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

Then, set the scheduler's quartz.scheduler.jobFactory.type property to your job factory's type.

For reference, here's the default job factory that quartz.net uses:

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

The interesting line is:

  return (IJob) ObjectUtils.InstantiateType(jobType);


Create a CustomJobfactory that overrides SimpleJobFactory and use spring to instantiate the job classes.

/// <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)
    {
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜