问题
I have a MVC4 .Net web application on 3-tier architecture with Unity dependency injection, and I want to shedule everyday a verficiation and send some mails where is the case. For this I want to use Quartz Scheduler in Application_start, because of the dependency injection windows service is not a good option.
Here is my code in application_start.
// construct a scheduler factory
ISchedulerFactory schedFact = new StdSchedulerFactory();
IScheduler sched = schedFact.GetScheduler();
sched.Start();
IJobDetail dailyUserMailJob = new JobDetailImpl("DailyUserMailJob", null, typeof(SchedulerJob));
// fire every time I open App/EveryDay
ITrigger dailyUserMailTrigger = new SimpleTriggerImpl("DailyUserMailTrigger", 1,
new TimeSpan(1, 0, 0, 0));
sched.ScheduleJob(dailyUserMailJob, dailyUserMailTrigger);
Here is my job code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using EvaluationMvc.Bll.Contracts;
using Quartz;
using Quartz.Impl;
namespace EvaluationMvc.Utils
{
public class SchedulerJob : IJob
{
private IEvaluationBus _iEvaluationBus;
public SchedulerJob(IEvaluationBus iEvaluationBus)
{
//Dependency injection
_iEvaluationBus = iEvaluationBus;
}
public void Execute(IJobExecutionContext context)
{
_iEvaluationBus.testingArchitecture();
// Sends a test mail.
}
}
}
However my job is never executed, what could be the problem ?
回答1:
Quartz.net Scheduler must be created as singleton.
You can install Unity.MVC4 NuGet Package.
It will create a Bootstrapper class which should look something like this:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
// Register your interfaces here.
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
}
}
Then you have to create your own implementation of JobFactory. This article might help you and this one is worth reading:
public class UnityJobFactory: IJobFactory
{
private readonly IUnityContainer container;
static UnityJobFactory()
{
}
public UnityJobFactory(IUnityContainer container)
{
this.container = container;
}
public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
var jobDetail = bundle.JobDetail;
var jobType = jobDetail.JobType;
try
{
return this.container.Resolve(jobType) as IJob;
}
catch (Exception ex)
{
throw new SchedulerException(string.Format(
CultureInfo.InvariantCulture,
"Cannot instantiate class '{0}'", new object[] { jobDetail.JobType.FullName }), ex);
}
}
public void ReturnJob(IJob job)
{
// Nothing here. Unity does not maintain a handle to container created instances.
}
}
and your own implementation of StdSchedulerFactory:
public class UnitySchedulerFactory : StdSchedulerFactory
{
private readonly UnityJobFactory unityJobFactory;
public UnitySchedulerFactory(UnityJobFactory unityJobFactory)
{
this.unityJobFactory = unityJobFactory;
}
protected override IScheduler Instantiate(QuartzSchedulerResources rsrcs, QuartzScheduler qs)
{
qs.JobFactory = this.unityJobFactory;
return base.Instantiate(rsrcs, qs);
}
}
Going back to your Unity Bootstrapper you have to register your interfaces:
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
container.RegisterType<ISchedulerFactory, UnitySchedulerFactory>(new ContainerControlledLifetimeManager());
container.RegisterType<IScheduler>(new InjectionFactory(c => c.Resolve<ISchedulerFactory>().GetScheduler()));
container.RegisterType<IQuartzScheduler, QuartzScheduler>(new ContainerControlledLifetimeManager());
container.RegisterType<IEvaluationBus, EvaluationBus>();
RegisterTypes(container);
return container;
}
I've wrapped up my service scheduler in a class so that I can create it singleton:
public interface IQuartzScheduler
{
void Run();
void Stop();
}
and:
public class QuartzScheduler : IQuartzScheduler
{
private readonly ISchedulerFactory SchedulerFactory;
private readonly IScheduler Scheduler;
public QuartzScheduler(ISchedulerFactory schedulerFactory, IScheduler scheduler)
{
this.SchedulerFactory = schedulerFactory;
this.Scheduler = scheduler;
}
public void Run()
{
IJobDetail dailyUserMailJob = new JobDetailImpl("DailyUserMailJob", null, typeof(Scheduler.SchedulerJob));
// fire every time I open App/EveryDay
ITrigger dailyUserMailTrigger = new SimpleTriggerImpl("DailyUserMailTrigger", 10,
new TimeSpan(0, 0, 0, 20));
this.Scheduler.ScheduleJob(dailyUserMailJob, dailyUserMailTrigger);
this.Scheduler.Start();
}
public void Stop()
{
this.Scheduler.Shutdown(false);
}
}
As you can see in this class I'll create my jobs/trigger and start the scheduler.
now in your Application_Start (global.asax) you can "bootstrap" your Unity Container, get the service scheduler and run it.
var unityContainer = Infrastructure.Bootstrapper.Initialise();
unityContainer.Resolve<IQuartzScheduler>().Run();
You can find a working sample following this link (QuartzWithUnity).
回答2:
Very useful, thanks LeftyX. I think, in Application_Start you have to create servise like this:
var unityContainer = Bootstrapper.Initialise();
QuartzScheduler jobService = (QuartzScheduler)unityContainer.Resolve(typeof(QuartzScheduler), "Jobs");
jobService.Run();
来源:https://stackoverflow.com/questions/17740048/quartz-net-trigger-does-not-fire-mvc4