Кварц, Unity & .NET - PullRequest
       38

Кварц, Unity & .NET

6 голосов
/ 12 сентября 2011

Можно ли зарегистрировать кварцевое задание, чтобы всегда использовать один и тот же экземпляр IJob, введенный контейнером DI Unity? У меня есть один «монитор» экземпляра класса Monitor из Unity DI, который я зарегистрировал как:

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

и моя реализация IJob ожидает, что этот экземпляр монитора будет внедрен в него:

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

но когда возникают кварцевые события, реализация IJob.Execute() вызывается до внедрения зависимости. Как мне заставить это работать? Стоит ли мне рассматривать другие DI-контейнеры или планировщики?

Спасибо

Ответы [ 4 ]

5 голосов
/ 23 апреля 2014

Посмотрите на Quartz.Unity.

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

Документ очень скудный, но, похоже, все, что вам нужно сделать, это добавить пакет nuget и следующую строку вконфигурация вашего контейнера.

var container = new UnityContainer().AddNewExtension<Quartz.Unity.QuartzUnityExtension>();
5 голосов
/ 12 сентября 2011

Кварц будет восстанавливать реализацию интерфейса работы при каждом пожаре. рекомендуется использовать IStatefulJob, если вы хотите сохранить состояние между выполнениями задания:

Экземпляры IStatefulJob следуют слегка отличающимся правилам от обычных экземпляров IJob.Ключевое отличие состоит в том, что их связанный с JobDataMap повторно сохраняется после каждого выполнения задания , таким образом сохраняя состояние для следующего выполнения.Другое отличие состоит в том, что задания с сохранением состояния не могут выполняться одновременно, а это означает, что новые триггеры, возникающие до завершения метода IJob.Execute, будут задержаны.

Из Кварца учебник :

StatefulJob

Теперь, некоторые дополнительные примечания о данных состояния задания (он же JobDataMap): Экземпляр задания можно определить как «с состоянием» или «без состояний».Для заданий без сохранения состояния JobDataMap хранится только в момент их добавления в планировщик.Это означает, что любые изменения, внесенные в содержимое карты данных задания во время выполнения задания, будут потеряны и не будут видны заданию при следующем его выполнении.Вы, наверное, догадались, задание с состоянием как раз наоборот - его JobDataMap повторно сохраняется после каждого выполнения задания.Одним из побочных эффектов создания задания с сохранением состояния является то, что оно не может быть выполнено одновременно.Или, другими словами: если задание с состоянием и триггер пытается «запустить» задание, когда оно уже выполняется, триггер заблокирует (подождет) до завершения предыдущего выполнения.

Вы «отметили»Задание с сохранением состояния, если оно реализует интерфейс StatefulJob, а не интерфейс Job.

Другой вариант для вас - реализовать собственный JobFactory:

Экземпляры Job ''

Еще один последний момент в этой теме, который может быть или не быть очевидным на данный момент: вы можете создать один класс заданий и сохранить его «определения экземпляров» в планировщике, создав несколько экземпляров JobDetails -каждый со своим собственным набором свойств и JobDataMap - и добавлением их всех в планировщик.

При срабатывании триггера создается экземпляр задания, с которым он связан, через JobFactory, настроенный в планировщике.Заданный по умолчанию JobFactory просто вызывает newInstance () для класса задания. Возможно, вы захотите создать собственную реализацию JobFactory для выполнения таких задач, как создание контейнера IoC или DI вашего приложения для создания / инициализации экземпляра задания .

2 голосов
/ 13 сентября 2011

Вы можете сделать это, внедрив свой собственный 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);
0 голосов
/ 10 ноября 2015

Создайте CustomJobfactory, который переопределяет SimpleJobFactory, и используйте 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)
    {
    }
}
...