Quartz, Unity & .NET

本文关键字:NET amp Unity Quartz | 更新日期: 2023-09-27 18:08:34

是否有可能注册石英作业始终使用DI容器Unity注入的相同IJob实例?我有一个来自Unity DI的类Monitor的单个实例"监视器",我注册为:

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

和我的IJob实现期望有监视器实例注入到它:

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

,但是当石英事件触发时,在注入依赖项之前调用IJob.Execute()实现。我该如何让它工作?我应该考虑其他DI容器或调度器吗?

谢谢

Quartz, Unity & .NET

Quartz将在每个火灾事件上重新建立作业接口实现。如果您希望在作业执行之间保留状态,建议使用IStatefulJob:

IStatefulJob实例遵循与regular略有不同的规则IJob实例。关键的区别在于它们的关联的JobDataMap在每次执行作业后重新持久化,从而保留下次执行的状态。另一个区别是有状态的作业不允许并发执行,这意味着有新的触发器在IJob完成之前发生的。执行方法将是推迟。

From Quartz tutorial:

StatefulJob

现在,关于作业状态数据(又名JobDataMap)的一些附加说明:作业实例可以定义为"有状态"或"无状态"。非状态作业只在它们运行时存储它们的JobDataMap被添加到调度程序中。这意味着对作业执行期间的作业数据映射内容将丢失,并且在下次执行时不会被任务看到。你有可能猜到了,有状态作业正好相反——它的JobDataMap在每次执行作业后重新存储。一个副作用使作业有状态是指它不能并发执行。或换句话说:如果一个作业是有状态的,并且一个触发器试图"触发"。当作业正在执行时,触发器将阻塞(等待)直到前一次执行完成。

你可以通过让一个作业实现statfuljob来将它"标记"为有状态的接口,而不是Job接口。

另一个选项是实现您自己的JobFactory:

工作"实例"

关于这个话题的最后一点,现在可能很明显,也可能不明显:您可以创建一个作业类,并存储多个实例通过创建多个实例在调度程序中定义它每个JobDetails都有自己的属性集和JobDataMap并将它们全部添加到调度程序中。

当触发器触发时,它所关联的作业通过实例化在Scheduler上配置的JobFactory。默认的JobFactory只需在作业类上调用newInstance()。您可能想要创建您自己的JobFactory实现来完成诸如让应用程序的IoC或DI容器生成/初始化 .

看看Quartz.Unity。

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

Doc是非常稀疏的,但是看起来您所需要做的就是将nuget包和下面的行添加到您的容器配置中。

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

您可以通过实现自己的JobFactory来实现这一点。您必须实现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);
}

然后,将调度程序的quartz.scheduler.jobFactory.type属性设置为作业工厂的类型。

作为参考,下面是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;
        }
    }
}

有趣的一行是:

  return (IJob) ObjectUtils.InstantiateType(jobType);

创建一个覆盖SimpleJobFactory的CustomJobfactory,并使用spring实例化作业类。

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