Home > CodeProject, Open Source, Silverlight, VFS > Lightweight Task Scheduling Library for .NET / Silverlight

Lightweight Task Scheduling Library for .NET / Silverlight

January 9th, 2010

I’m currently working on VFS, a virtual file system. For running transfers, VFS internally maintains locks that do have expiration time. Accordingly, I found myself in need for a job scheduling mechanism in order to properly release expired locks. I looked around for a few alternatives, but eventually ended up writing my own lightweight version.

Features:

  • Simple scheduling and callback mechanisms
  • Silverlight compatible
  • Lightweight
  • Fluent API
  • Detection of system time changes with optional rescheduling
  • Optional forwarding of exceptions during job execution
  • Open Source (Ms-PL)

 

Download library and sample

Current Version: 1.0.2, 2010.01.12

 

What Does it Do?

Basically, the library allows you to create a job, and submit that job to a scheduler, along with a callback action. This callback action is invoked as soon (or every time) the job is due.

Before going into the details, here’s a first code snippet that creates a simple Job that is supposed to run repeatedly (every 1.5 seconds) for a minute. Once the job is created, it is submitted to a Scheduler instance which processes the job and makes sure the submitted callback action is being invoked every time the job is due:

private Scheduler scheduler = new Scheduler();

public void RunOnce()
{
  //define a start / end time
  DateTime startTime = DateTime.Now.AddSeconds(5);
  DateTime endTime   = startTime.AddSeconds(60);

  //configure the job
  Job consoleJob = new Job();
  consoleJob.Run.From(startTime)
                .Every.Seconds(1.5)
                .Until(endTime);

  //submit the job with the callback to be invoked
  scheduler.SubmitJob(consoleJob, j => Console.Out.WriteLine("hello world"));
}

 

Silverlight

The project provides class libraries for .NET 3.5 and Silverlight 3, along with a Silverlight sample application that shows how to add scheduling functionality to your SL application with just a few lines of code.

While long-term scheduling isn’t probably something you need to do in a Silverlight application, the scheduler simplifies the management of periodic jobs, such as polling a server for updates. Below is a snippet from the Silverlight sample application. This job starts immediately, and runs indefinitely with an interval of 2 seconds:

private void CreateJob2()
{
  //create job
  Job<int> job = new Job<int>("Job 2");
  job.Data = 0;
  job.Run.Every.Seconds(2);

  //submit to scheduler
  scheduler.SubmitJob(job, LogJobExecution);
}


private void LogJobExecution(Job<int> job, int data)
{
  //updates user interface
}

 

image

 

Jobs

 

A job is a simple configuration item for the scheduler. There’s two built-in job types: Job and Job<T>. The only difference between the two is that the latter provides a Data property which allows you to attach state information directly to the job and have it delivered back to you when the job runs.

 image

 

Creating Jobs

Creating a job is pretty easy, and can be configured through a fluent interface (optional, you can also set the properties the traditional way). Jobs may run once, several times, or indefinitely – you can configure intervals, an optional expiration time, or a maximum number of executions. Here’s a few configuration samples:

 

//run the job every second until we manually cancel it
Job job = new Job();
job.Run.Every.Seconds(1);

 

//create a job with an attached FileInfo
Job<FileInfo> job = new Job<FileInfo>();
job.Data = new FileInfo(@"C:readme.txt");

//run the job 5 times every 1.5 seconds, start immediately
TimeSpan interval = TimeSpan.FromSeconds(1.5);
job.Run.Every.TimeSpan(interval).Times(5);

 

//create a simple job
Job job = new Job();

//run the job every 1.5 seconds until it expires
DateTime expiration = DateTime.Now.AddMinutes(5);
job.Run.Every.Seconds(1.5).Until(expiration);

 

The Scheduler Class

 

Jobs are being submitted to the Scheduler class, which provides a very simple API. Most methods should be pretty self-explanatory, and they are well documented.

image

 

Callback Actions

If you add a Job to the Scheduler through one of the SubmitJob overloads, you have to submit a callback action that is being invoked whenever the job runs. Depending on whether you use Job or Job<T>, a different callback delegate may be used:

 

Job Class Callback Action
Job Action<Job>
Job<T> Action<Job<T>, T>

 

Here’s a sample that uses two different callback methods:

class Program
{
  private static Scheduler scheduler = new Scheduler();

  static void Main(string[] args)
  {
    Job simple = new Job("simple");
    simple.Run.From(DateTime.Now.AddSeconds(3)).Once();
    
    Job<int> generic = new Job<int>("generic") {Data = 123};
    generic.StartAt(DateTime.Now.AddSeconds(1)).Run.Once();

    scheduler.SubmitJob(simple, HandleSimpleJob);
    scheduler.SubmitJob(generic, HandleGenericJob);

    Console.ReadLine();
  }

  private static void HandleGenericJob(Job job, int data)
  {
    Console.Out.WriteLine("Job '{0}' submitted data: {1}", job.Id, data);
  }

  private static void HandleSimpleJob(Job job)
  {
    Console.Out.WriteLine("Simple job '{0}' was executed.", job.Id);
  }
}

 

The sample above produces the following output on the console:

image

 

Cancelling or Pausing a Job

If you want to cancel or pause a job, you can do this either via the Scheduler class, or directly on the Job instance that is submitted to you whenever the job runs. Note that canceling the job is a terminal operation – a canceled job cannot be resumed.

private void MyJobCallbackAction(Job job)
{
  //stops execution but keeps the job alive
  job.Pause();

  //resumes operation of a paused job
  job.Resume();

  //terminates the job
  job.Cancel();
}

 

Actually, doing these operations directly via the Job class is the preferred mechanism – if you use the scheduler’s methods, the scheduler needs to lock its internal list and search for the job itself, which costs you processing power.

 

Exception Handling

An exception that occurs during the execution of a job does not affect the scheduler – all jobs are being executed on worker threads taken from the .NET thread pool. However, in order not to miss any exceptions, you can instruct the Scheduler class to supervise executing jobs, and forward any unhandled exceptions to a single exception handling routine. All you need to do is registering a callback action to the JobExceptionHandler of the Scheduler class:

private Scheduler scheduler;

private void InitScheduler()
{
  scheduler = new Scheduler();
  scheduler.JobExceptionHandler = LogJobException;
}

private void LogJobException(Job job, Exception exception)
{
  string msg = "Exception occurred while executing job {0}: {1}";
  msg = String.Format(msg, job.JobId, exception.ToString());
  MyLogger.LogError(msg);
}

 

Performance

 

The library could surely be tweaked, but it performs pretty well. Running 10000 jobs, each with an interval of 100 ms (that’s around 100 jobs per millisecond) keeps the CPU busy (at around 25% on my machine), but neither eats away your memory nor freezes the PC. Bigger intervals aren’t a problem at all because the scheduler is sleeping most of the time.

 

Reacting to System Time Change Events

 

Assume the following:

  • The current time is 21:00, your job is scheduled to run at 22:00
  • The user changes the computer’s time to 21:30

Now, depending on your needs, you might want the scheduler to adjust itself based on two strategies:

  • A: If you want the job to run at a fixed time (22:00), you expect the job to run in 30 minutes. This is the scheduler’s default behavior.
  • B: If you want the job to run based on relative times, you still expect the job to run in an hour, so the execution time would have to be change to 22:30.

In order not to miss such an event, the scheduler performs a quick self test with a fixed configurable interval from time to time – even if no jobs are up for execution. Per default, the scheduler’s SelfTestInterval property is set to two minutes, but this can be configured should you need more (or less) accurate re-scheduling.

If you want the scheduler to just readjust its internal schedule and keep fixed times (A), you don’t have to do anything. However, if you want the scheduler to reschedule its jobs if a system time change was detected (B), you can do by setting the SystemTimeChangeRescheduling property of the Scheduler class, which takes an enum value of type ReschedulingStrategy:

 image

As you can see, you can choose not to reschedule at all (default), only reschedule the next execution time (the next time the job runs), or even shift the expiration time of your jobs.

Fixed vs. Relative Rescheduling Depending on Jobs

What if you have a few jobs that should run on a fixed time, while others should be rescheduled?

I decided against making the API more complicated by allowing jobs to be individually configured. As an alternative, I’d suggest to just use two scheduler classes with individual configurations. You could even write a wrapper class that just maintains two schedulers, and forwards all job submissions to the correct one. Here’s a quick but working implementation:

public class MyScheduler
{
  private readonly Scheduler fixedSchedules = new Scheduler();
  private readonly Scheduler relativeSchedules = new Scheduler();


  public MyScheduler()
  {
    //configure relative rescheduling
    var strategy = ReschedulingStrategy.RescheduleNextExecution;
    relativeSchedules.SystemTimeChangeRescheduling = strategy;
  }

  //This method just forwards submitted jobs to one of the schedulers
  public void SubmitJob(Job job, Action<Job> callback, bool hasFixedSchedule)
  {
    if(hasFixedSchedule)
    {
      fixedSchedules.SubmitJob(job, callback);
    }
    else
    {
      relativeSchedules.SubmitJob(job, callback);
    }
  }
}

 

Persisting Jobs

 

Persisting jobs is not part of the library, but could be done by subclassing the Scheduler class. The class gives you protected access to all its internals, including its job list. Job is a very simple component, so it should be fairly easy to store and reload all jobs.

However, from an architectural point of view, I’d probably not persist any jobs at all, but rather recreate them during initialization based on persisted business data. A job does execute in a given context, because of “something”. Let’s assume this “something” is a reminder flag in a calendar application:

  • User sets a reminder on a calendar entry.
  • Calendar application updates database record of the calendar entry, then creates a job with the scheduler.
  • User turns off application – the scheduler and running jobs are being disposed.
  • User restarts application.
  • The application retrieves all reminders from the database and schedules jobs for them.

This approach is simple, and clearly defines the responsibilities of the components in your application. It also minimizes dependencies on the scheduling system.

 

Implementation Notes

 

Creating a Fluent Interface

The library provides a fluent API that allows you to chain configuration settings:

//job starts at a given start time, repeats until expiration time
Job myJob = new Job();
myJob.Run.From(startTime).Every.Seconds(1.5).Until(expiration);

 

The fluent API is optional – a job can also be configured via traditional properties setters. However, the corresponding declaration is more verbose:

//job starts at a given start time, repeats until expiration time
Job myJob = new Job
              {
                Loops = null,
                StartTime = startTime,
                Interval = TimeSpan.FromSeconds(1.5),
                ExpirationTime = expiration
              };

 

Basically, the implementation of the fluent API is very simple:

  • The Run property returns an instance of a helper class called JobSchedule. This helper class provides methods such as From, Until, Every, or Once.
  • All methods of the JobSchedule class have a return value of JobSchedule again, which allows the developer to chain the operations as in the sample above.

Below is a part of JobSchedule’s implementation. You can see that the class receives an instance of type Job, and just sets the properties on this class.

public class JobSchedule
{
  private readonly Job job;

  //created with the job that is being configured
  public JobSchedule(Job job)
  {
    this.job = job;
  }


  //all operations just return the schedule itself again
  public JobSchedule From(DateTimeOffset startTime)
  {
    job.StartTime = startTime;
    return this;
  }

  ...

}

 

 

DateTime, DateTimeOffset, and SystemTime

SystemTime Pattern

With a scheduler, everything is about time. However, time-related code is very hard to test, which is why the scheduler does not directly access the current system time, but makes use of Ayende’s SystemTime. This is an awesome pattern, and I highly recommend using it whenever timestamps play an important role in code.

 

DateTimeOffset vs. DateTime

You might have noticed that usually, the code works with DateTimeOffset rather then the commonly known DateTime struct. DateTimeOffset is basically an alternative to DateTime, but operates on UTC which makes it an ideal candidate for most time-related scenarios. You can read more about it at MSDN or at the BCL team blog.

However, you can even still use DateTime in your code if you feel more comfortable with it:

Job job = new Job();

DateTimeOffset expDate1 = DateTimeOffset.Now.AddHours(2);
DateTime       expDate2 = DateTime.Now.AddHours(2);

//the job takes both DateTimeOffset and DateTime values
job.ExpirationTime = expDate1;
job.ExpirationTime = expDate2;

 

 

Job List, Timer and Pooled Execution

Job List

Internally, the Scheduler class uses a very simple mechanism: All jobs are being cached in a sorted list, which allows the scheduler to easily determine the next execution time. If a new job is being submitted, the scheduler only has to check whether the new job runs before the next scheduled job or not. If yes, the scheduler adjusts its next execution. If no, the job is just added to the end of the list, and a flag is set that reminds the scheduler to reorder the list once the next job was done. This approach has a big advantage: Even if many jobs are being submitted, reordering takes only place once the next job runs.

 

public void SubmitJob(Job job, Action<Job> callback)
{
  //[validation omitted for brevity]

  JobContext context = new JobContext(job, callback);
  
  lock(syncRoot)
  {
    //if this job is going to be the next one, we need to reconfigure
    //the timer. Do not reschedule if the next execution is imminent
    if (NextExecution == null || context.NextExecution <= NextExecution.Value)
    {
      //insert at index 0 –> makes sure the job runs first on next timer event
      jobs.Insert(0, context);

      //only reschedule if the next execution is not imminent
      if (NextExecution == null || NextExecution.Value.Subtract(SystemTime.Now())
                                                .TotalMilliseconds > MinJobInterval)
      {
        //no sorting required, but we need to adjust the timer
        Reschedule();
      }
    }
    else
    {
      //add at end of the list and mark list as unsorted
      //the job will be sorted and rescheduled on the next run (which is before
      //this job's execution time)
      jobs.Add(context);
      isSorted = false;
    }
  }
}

 

 

Timer and Rescheduling

In order to trigger the execution, a single Timer is used to wake up the scheduler once jobs are due. The timer is set to one of those values:

  • If the scheduler has no jobs at all, the timer is disabled.
  • If jobs are pending, the next interval is the execution time of the next pending job.
  • If the self test interval is lower than the execution time of the next job, the scheduler will run an evaluation at this time (which ensures that a changed system time does not cause the scheduler to oversleep).

The whole timer logic is encapsulated in the Reschedule method:

/// <summary>
/// Reconfigures the timer according to the
/// next pending job execution time.
/// </summary>
private void Reschedule()
{
  if(jobs.Count == 0)
  {
    //disable the timer if we don't have any pending jobs
    NextExecution = null;
    timer.Change(Timeout.Infinite, Timeout.Infinite);
  }
  else
  {
    //schedule next event
    var executionTime = jobs[0].NextExecution;

    DateTimeOffset now = SystemTime.Now();
    TimeSpan delay = executionTime.Value.Subtract(now);

    //in case the next execution is already pending, add a safe delay
    long dueTime = Math.Max(MinJobInterval, (long)delay.TotalMilliseconds);

    //run at least with the self testing interval
    dueTime = Math.Min(dueTime, SelfTestInterval);

    NextExecution = SystemTime.Now().AddMilliseconds(dueTime);
    timer.Change(dueTime, Timeout.Infinite);
  }
}

 

Job Execution

“Executing” a job basically means invoking the callback action that was submitted along with the job. This always happens asynchronously through the .NET thread pool:

/// <summary>
/// Invokes the managed job's <see cref="CallbackAction"/> through
/// the thread pool, and updates the job's internal state.
/// </summary>
public void ExecuteAsync()
{
  //only execute if the job is active
  if (...)
  {
    ThreadPool.QueueUserWorkItem(s => CallbackAction(ManagedJob));
  }

  UpdateState();
}

 

Conclusion

 

This is a neat little helper library, and its small footprint makes it a viable alternative to handling timers yourself even for small applications. Happy coding 🙂


  1. January 10th, 2010 at 09:26 | #1

    Great stuff here Philip

  2. January 10th, 2010 at 09:33 | #2

    Cheers Sacha 🙂

  3. January 11th, 2010 at 00:05 | #3

    Quite a nice little library. I hope I’ll remember it when I actually need something like this.

  4. Peter
    January 12th, 2010 at 16:51 | #4

    Thanks for posting the code and tests. Nicely done. I just implemented something very similar for a project I’m working on. Two things I don’t see here are logging or error handling. What if a job fails during execution?

  5. January 12th, 2010 at 17:01 | #5

    Peter,

    Edit: Exception handling implemented and published with version 1.0.2. Thanks for the feedback, Peter 🙂

    I’d rather see the responsibility to handle failed jobs within the client code (on the callback method). However, I’m thinking about adding a callback handler for exceptions into the Scheduler class – mainly in order to handle potential issues with a failing ThreadPool (OutOfMemoryException), but I might extend the mechanism in order to report job exceptions as well. Hmm, need to think about this first…

  6. jbland
    January 12th, 2010 at 18:06 | #6

    Is this a good fit for ASP.NET ?

  7. January 12th, 2010 at 18:08 | #7

    @jbland
    No problem if it’s supposed to run server-side. Also make sure you will get the upcoming update (as of v1.0.2), which provides a simple exception handling mechanism.

  8. Miha Novak
    January 13th, 2010 at 00:06 | #8

    Nice implementation, may I just ask what was it that convinced you to write your own task scheduler – what was it about Quarz.NET that you didn’t you find adequate?
    I’we always had at the back of my mind that Quartz.NET could probably cover all my needs, was it perhaps just the API in your case?

  9. January 13th, 2010 at 00:44 | #9

    Miha,

    Quartz looked like an obvious choice from the specs, but it turned out to be too heavyweight for me. It has maintained compatibility back to .NET 1.1, and also comes with a dependency to Common.Logging that was a bit of a show stopper to me. And Silverlight compatibility is of course out of the question as well.

    However, this doesn’t mean that my library is necessarily the better solution for your project: I guess my approach and Quartz just satisfy different requirements: Quartz is quite a beast and brings a lot to the table in one package (including database storage). Mine is a leightweight solution that can be *very* easily integrated and adjusted if needed, and leverages new language features in order to provide a lean API.

    HTH,
    Philipp

  10. January 17th, 2010 at 03:27 | #10

    Great! keep up your good work.
    How to enumerable to the job collection?

  11. January 17th, 2010 at 11:08 | #11

    Mac,
    The scheduler’s internal job collection is not public, because tampering with it might be delicate in terms of performance and/or multithreading.
    In order to make the jobs public, you would have to subclass the Scheduler class like this (note that the collection is locked during enumeration, and does not return the internal list itself):

    public class MyScheduler : Scheduler
    {
      public IEnumerable<Job> GetJobs()
      {
        lock(SyncRoot)
        {
          return Jobs.Select(jc => jc.ManagedJob).ToArray();
        }
      }
    }
  12. rm
    March 8th, 2010 at 14:09 | #12

    Hi Phillip,

    very interestign job you have here 🙂

    I need something like that but to schedule workflow from a data base… in diferent periods of time..

    some suggest?

    thanks i will now look at your sample with more attention

  13. March 8th, 2010 at 15:11 | #13

    I wrote down a few thoughts in the section “Persisting Jobs”. I would recommend you to initialize your jobs during application initialization based on your individual business logic, and possible reschedule (by creating new jobs) as necessary.
    You could even have a sort of “controller instance” that is responsible for scheduling your jobs being invoked by a job itself – in the end, it depends on your individual needs.

  14. Stephen Kennedy
    March 10th, 2010 at 20:00 | #14

    This is a very elegant lightweight scheduling solution Philipp.

    I’m inheriting from your classes and building my own payload objects in order to achieve something similar to comment #12 (scheduling from a database).

    I don’t want to alter your original source code in case you release new versions. However, there’s one thing missing for me – it’s not possible to override the job execution from within a Scheduler-derived class (I want to fire a log method in my job classes). Would you consider doing the following?

    In JobContext.ExecuteAsync remove the if statement and put the same code into a property:

    public bool WillExecute
    {
    get
    {
    return ManagedJob.State == JobState.Active && (ManagedJob.ExpirationTime == null || ManagedJob.ExpirationTime >= SystemTime.Now());
    }
    }

    In Scheduler.RunPendingJobs() replace currentJob.ExecuteAsync(this); with
    if (Job.WillExecute)
    ExecuteJob(currentJob);
    and add the following method:

    protected virtual void ExecuteJob(JobContext Job)
    {
    Job.ExecuteAsync(this);
    }

  15. Cuong Le
    March 16th, 2010 at 19:17 | #15

    Very nive tool, it helps me a lot

  16. CJ
    December 9th, 2010 at 11:06 | #16

    This looks very very handy since I’m planning a “ToDo notifier”.
    I’m interested in how the jobs are called by the scheduler. Are you using a Windows Service? Or does it require a program running in the background, providing the scheduler interface? I didn’t find any information on this in this article, so I hope I can get a quick answer from your highness.
    Also, awesome everything on everywhere I found in your blog. You are a very valuable man, sir :3

  17. December 10th, 2010 at 04:34 | #17

    Hi CJ

    Thanks for your kind words – I’m glad you found some useful stuff here @ hardcodet 🙂

    Regarding your question: The scheduler is just a component that internally manages a timer. So there’s no services or anything like that. This means that if you want to use it with your notifier, the notifier needs to run in the background and just register its jobs during startup.

    Check the article above – I’ve left some thoughts on this in the “Persisting Jobs” section.

    Cheers,
    Philipp

  18. Rahul Dhamlale
    January 3rd, 2011 at 11:12 | #18

    I am scheduling the task using above methods. But when the application runs and jobs again schedules then they executed immediately and not as per the time which I have given at the time of creating jobs.

    E.g. If i schedule job every Monday at 8PM
    and if application restart then it takes the time which we logged into the system and execute and then next period sets from current Login time.

    So, Help me out of this problem.

  19. Fergus Bown
    January 7th, 2011 at 11:21 | #19

    Hi,

    We’re using the scheduler library, and spotted what looks like an error (unless I’m being dim!) in VerifySystemTime.

    The code says:

    if(delta > 1000 || delta 1000)
    {
    reschedule

    i.e. equivalent to

    if(delta != 1000)
    {
    reschedule

    presumably should be something like:

    if(Math.Abs(delta) > 1000)
    {
    reschedule

    (apologies for the double post, but half my comment didn’t appear!)

    thanks

    Fergus

  20. Stephen Kennedy
    March 16th, 2011 at 14:43 | #20

    Hi Philipp,

    I’ve been studying the code again for a couple of hours and I can’t see where the scheduler removes cancelled jobs from the Jobs collection, except in UpdateState() which is called from ExecuteAsync() which only ever runs when the job is executed so far as I can tell. What if the job has an execution time in the distant future – don’t we want to get rid of it before then? (the answer I’m sure is yes, so I’m probably missing something). Also in CancelJob() we don’t set IsSorted to false, will this cause problems if the cancelled job is actually Jobs[0]? Reschedule() doesn’t do any sorting it just grabs Jobs[0]; if the list hasn’t been sorted the new Jobs[0] could be any old random job couldn’t it, not necessarily the one due to run next?

  21. March 16th, 2011 at 17:17 | #21

    Stephen

    I will have to look into it. I’m currently abroad but will find time once I’m back (end of March). For now, thank you very much for your input – it’s highly appreciated!

    Cheers,
    Philipp

  22. Krishnapratap Vedula
    March 23rd, 2011 at 06:21 | #22

    Hello Philipp Sumi,

    How can I use Lightweight Scheduler on Shared Hosting Server in my ASP.NET page without requesting it like Windows Service? I mean from 9 AM to 4 PM. Thanks.

  23. David
    July 4th, 2011 at 04:14 | #23

    Hi Philipp. First of all, thank you for this great work. Now, I have a question about job execution. What happens if I schedule a job to execute every second and job requires ~2 seconds to finish? another instance of job will be created and executed? or scheduler will wait until the job finishes? Thanks

  24. David Rodriguez
    October 18th, 2011 at 08:04 | #24

    How do you prevent job concurrency? If a critical job is executing every 15 minutes but taking longer than expected; how do you prevent the same job from executing again until the previous one has finished?

  25. November 1st, 2011 at 02:09 | #25

    @Fergus Bown
    Thanks for the hint, Fergus! This was clearly a bug. Fixed with the latest version.

  26. November 1st, 2011 at 02:11 | #26

    @Stephen Kennedy
    Stephen,

    I agree that there’s value in removing canceled jobs right away, but a job is a very lightweight object, and the scheduler performs a self-test every 2 minutes to sanitize itself, so there’s no risk of jobs lurking around. This justifies keeping things simple.
    However, you nicely spotted the issue with the first evaluated job being canceled – this was a bug that has been fixed. Thanks!

    Cheers,
    Philipp

  27. Andrew Everson
    November 17th, 2011 at 20:58 | #27

    Hi, I see you made some recent fixes but I cant see a link to the new code besides the Current Version: 1.0.2, 2010.01.12 . Thanks for a great library.

  28. April 28th, 2012 at 03:37 | #28

    Good job. (No pun intended).

    Open source contributors like you are very valuable and appreciated.

  29. July 7th, 2012 at 06:07 | #29

    Philipp, why not host the source code on CodePlex, GitHub, or any other place and allow contributions?

  30. Mladen Mihajlovic
    May 23rd, 2013 at 14:13 | #30
  31. Greg
    June 20th, 2013 at 11:23 | #31

    Hi Philipp,

    This looks to be exactly what I need.
    Not being a true programmer myself, could you tell me if your scheduler would work ok as part of a vb.net project?
    Also, post #25 implies there is a newer version available, if so could you point me in the right direction please? The download on this page seems to still be version 1.0.2.

    Thanks.

  32. Rob
    September 18th, 2013 at 18:43 | #32

    Philipp, this is great stuff!!! this is what is have been looking for… Please tell me if you think this would work with my website. Basically, i need to schedule 4-task: 1. Start, 2. CheckStatus, 3. End @ 4. CheckReaults. these task could be evoked over 3 to 24 days (star to finish) Please see below:

    So, I have a website that runs a kind-of auction. I would like to schedule a number of tasks based on the following factors… any help would be greatly appreciated. Thanks.

    1. Auction StartDate = DateTime.Now (or some date in the future)
    2. Auction Duration = x days (where “x” is either; 3, 5, 7, 10 or 14)
    3. Auction EndDate = StartDate + Duration
    4. ClosingPeriod Duration = 7 days (this is a fix number of days)
    5. ClosingPeriod StartDate = Auction EndDate
    6. Deal Status CheckDate = Auction EndDate – 2 days
    7. Total Transaction Period = Auction Duration + ClosingPeriod Duration (from the 1st to 4th task).

    The Process is as follows:

    TASKS:
    First: START Auction
    //Do something

    Second: Status CheckDate
    //Do something

    Third: END Auction (Note, auction close at 12 noon in ALL (6) timeZones across in U.S.)
    //Do something
    START ClosingPeriod

    Forth: END ClosingPeriod
    //Do something

    My website is in ASP.NET Razor, so what are my options. I assume I will need a dedicated server? I am new to asp.net and programming (only 4-weeks) so be as detailed as possible. Thanks.

    Rob

  33. Rob
    September 18th, 2013 at 18:44 | #33

    @Philipp Sumi

    Philipp, this is great stuff!!! this is what is have been looking for… Please tell me if you think this would work with my website. Basically, i need to schedule 4-task: 1. Start, 2. CheckStatus, 3. End @ 4. CheckReaults. these task could be evoked over 3 to 24 days (star to finish) Please see below:
    So, I have a website that runs a kind-of auction. I would like to schedule a number of tasks based on the following factors… any help would be greatly appreciated. Thanks.
    1. Auction StartDate = DateTime.Now (or some date in the future)
    2. Auction Duration = x days (where “x” is either; 3, 5, 7, 10 or 14)
    3. Auction EndDate = StartDate + Duration
    4. ClosingPeriod Duration = 7 days (this is a fix number of days)
    5. ClosingPeriod StartDate = Auction EndDate
    6. Deal Status CheckDate = Auction EndDate – 2 days
    7. Total Transaction Period = Auction Duration + ClosingPeriod Duration (from the 1st to 4th task).
    The Process is as follows:
    TASKS:
    First: START Auction
    //Do something
    Second: Status CheckDate
    //Do something
    Third: END Auction (Note, auction close at 12 noon in ALL (6) timeZones across in U.S.)
    //Do something
    START ClosingPeriod
    Forth: END ClosingPeriod
    //Do something
    My website is in ASP.NET Razor, so what are my options. I assume I will need a dedicated server? I am new to asp.net and programming (only 4-weeks) so be as detailed as possible. Thanks.
    Rob

  34. September 18th, 2013 at 19:34 | #34

    @Rob
    You might want to look at Workflow Foundation for complex workflows. Other than that, you’d also have to think of persisting your jobs etc. Given that those workflows are the core of your application, I’m not sure this *lightweight* framework is the way to go there…

  35. Gus
    December 17th, 2013 at 20:23 | #35

    Hi,

    How could I access the data in job.Data in the OnJobExection? Am I thinking wrong?

    Job job = new Job();

    job.Data = new Monitor(“191.168.147.21”, “NKI”);
    job.JobId = “Service”;

    job.Run.From(startTime).Once();

    //submit the job with the callback to be invoked
    scheduler.SubmitJob(job, OnJobExecution);
    }

    private void OnJobExecution(Job job)
    {

    }

  1. January 12th, 2010 at 09:51 | #1
  2. October 11th, 2011 at 12:16 | #2
  3. March 7th, 2016 at 14:31 | #3