I'm working on a windows service that has a need to perform some tasks each night. I really didn't want to have to write some mundane threading code that woke up periodically and checked the time. I also wanted to use my existing Spring.Net and NHibernate infrastructure to perform the tasks.

After a bit of research, I came across Quartz.Net. Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems...basically, it was just what I was looking for.

Spring.Net has several hooks to create tasks and a couple of different configuration options.

First, you need to create a class that will be used for your job.

namespace MyService
{
    public class RecurringJob : QuartzJobObject
    {
        private ILog log;

        public IMyService MyService { get; set; }

        protected override void ExecuteInternal(JobExecutionContext context)
        {
            PerformJob();
        }

        private void PerformJob()
        {
            if (log == null)
            {
                log = log4net.LogManager.GetLogger("MyLogger");
            }
            
            log.Info(string.Format("Performing Job at {0}", DateTime.Now));
            
// put real code here } } }

The important thing to not is that this class inherits from QuartzJobObject and overrides ExecuteInternal. This base class is provided by Spring.Net. You'll also need to reference the Quartz DLL.

Next, we'll need to configure our Spring IOC Container. You'll need to add a few objects.

<object id="MyService"
        type="MyServices.MyService, MyServices"
        autowire="byName">
        <constructor-arg index="0" ref="MyRepository" />
</object>

This is a simple object declaration to my service (which has is setup via constructor injection with a NHibernate repository...if you need to know how to set this up, see my other posts).

Next, we need to configure the actual job. It is declared as follows:

<object name="RecurringJob" type="Spring.Scheduling.Quartz.JobDetailObject, Spring.Scheduling.Quartz">
   <property name="JobType" value="MyService.RecurringJob, MyService" />
  <property name="JobDataAsMap">
    <dictionary>
      <entry key="Stateful" value="true" />
    </dictionary>
  </property>
</object>

So, a couple of things here. First, the object that will be used for my job is not actually declared in the IOC container. It's simply a string that represents the type the job will instantiate. Second, you can pass some static data (string, int, bool) to the class by using the JobDataAsMap property. Notice, I set the Stateful property (which exists on the QuartzJobObject base class) so that the scheduled job would be completed before the next job fires. Otherwise, the next scheduled job could start executing before the previous one has completed (in my case, I only want the job to run once a day so it's not a big deal but it COULD be in certain circumstances).

Now, we need to actually setup the schedule for the job. There are two ways to do this (I'll show you both):

<object id="SimpleTrigger" type="Spring.Scheduling.Quartz.SimpleTriggerObject, Spring.Scheduling.Quartz">
  <property name="JobDetail" ref="RecurringJob" />

  <!-- 10 seconds -->
  <property name="StartDelay" value="10s" />

  <!-- repeat every 5 seconds -->
  <property name="RepeatInterval" value="5s" />
</object>

<object id="CronTrigger" type="Spring.Scheduling.Quartz.CronTriggerObject, Spring.Scheduling.Quartz">
  <property name="JobDetail" ref="RecurringJob" />

  <!-- run every morning at 6 AM -->
  <property name="CronExpressionString" value="0 0 6 * * ?" />
</object>

The first is a "simple" trigger that essentially defines how long to wait once the app starts up before starting (the "StartDelay" property) and how long to wait between starting each instance of the task at hand (the "" property). You'll also need to set the "JobDetail" property to the instance of your job (that we defined above).

The second is more of an actual scheduled job (the "CronTriggerObject"). In this example, we set the "JobDetail" property just like the simple trigger, but then we set the CronExpxressionString property. This example sets the job to run at 6:00 am each day. For more information on the CronExpressionString syntax, here's a tutorial: http://www.quartz-scheduler.org/docs/tutorials/crontrigger.html.

The last thing to setup is the Quartz Scheduler Factory:

<object id="quartzSchedulerFactory" type="Spring.Scheduling.Quartz.SchedulerFactoryObject, Spring.Scheduling.Quartz">
   <property name="SchedulerContextAsMap"> 
      <dictionary> 
         <entry key="MyService" value-ref="MyService" /> 
      </dictionary> 
   </property> 
   <property name="triggers"> 
      <list> 
         <ref object="CronTrigger" /> 
         <ref object="SimpleTrigger" /> 
      </list> 
   </property> 
</object>

Let's discuss the easy part first, set the trigger property to the triggers we previously declared (I've set up both variations but you really only need one). Just comment or delete the one you don't want to use.The most time consuming part of this entire exercise was figuring out how to take advantage of dependency infection in my service. After a couple of hours of research, I finally found the "ScheduleContextAsMap" property. Each instance of the job is technically a new object so attempting to set properties as part of the actual job declaration will not blow up, it just won't work. Here, I specify my instance of the object that I want to inject into my object (this time using setting injection). Remember, the "key" is the property on my job class and the "value-ref" represents the id/name of the object that is declared in the container.

When you crank up your app and instantiate the ApplicationContext, the magic happens and your job begins to execute. That's all there is to it.