Using FluentScheduler with C# for Task Scheduling
FluentScheduler is a .NET library that allows you to schedule tasks and jobs in a fluent, readable way. It can be used to create robust and reliable schedulers for running recurring tasks or one-off jobs. This post will look at how to get started with FluentScheduler in a C# application.
Overview of FluentScheduler
FluentScheduler provides a fluent interface for defining jobs and their triggers and facilities for monitoring and managing jobs. Key features include:
- The declarative syntax for defining jobs and schedules
- Support for one-time and recurring jobs
- Cron and calendar-based scheduling
- Job State monitoring and logging
- Background thread for running jobs
- Automatic retry of failed jobs
- Job priorities and grouping
- Preventing concurrent job execution
The library handles the complexity of scheduling behind the scenes, allowing you to focus on the tasks and logic specific to your application. The fluent API results in scheduler configuration code that is easy to read and maintain.
Getting Started
To use FluentScheduler, you’ll first need to install the FluentScheduler NuGet package into your C# project.
After installing the package, the first step is to define a job class that represents a task or units of work you want to schedule:
public class SampleJob : IJob
{
public void Execute()
{
// Task logic goes here
}
}
This class simply needs to implement the IJob
interface, which requires a Execute()
method. This method will contain whatever logic your job needs to run.
Next, you create a scheduler instance and register your job class:
var scheduler = new StdSchedulerFactory().GetScheduler();
scheduler.Schedule<SampleJob>().ToRunNow();
Here we:
- Instantiate the scheduler
- Use
Schedule()
to register ourSampleJob
class with it - Call
ToRunNow()
to execute the job immediately
This will cause the scheduler to create an instance of SampleJob
and invoke its Execute()
method.
Instead of running immediately, we can schedule it to run on a particular schedule:
scheduler.Schedule<SampleJob>()
.DailyAt(8, 0)
.OnEvery(1).Days();
Now our job will run every day at 8 AM.
Defining Jobs
The IJob
interface that jobs implement requires a single Execute()
method for the task logic. Typically, you'll want to define jobs by inheriting from the JobBase
class:
public class SampleJob : JobBase
{
public override void Execute()
{
// Job logic
}
}
This provides additional capabilities like tracking the last and next run times of the job.
Job classes are POCOs with no dependencies on FluentScheduler, so they can contain whatever logic your application requires:
public class CleanupJob : JobBase
{
public override void Execute()
{
// Clean up old files
DeleteFilesOlderThan(TimeSpan.FromDays(30));
// Log success
Logger.LogInfo("Cleanup complete.");
}
}
Any exceptions thrown by the Execute()
method will be caught and handled gracefully by the scheduler.
Job Data and Context
You can also pass data or context into job instances by passing params to the Schedule()
method:
scheduler.Schedule<SampleJob>(someContext, someData)
.DailyAt(8, 0)
.OnEvery(1).Days();
These will be available via the JobDataMap on the job instance:
public override void Execute()
{
var context = JobDataMap.Get<SomeContext>(0);
var data = JobDataMap.Get<SomeData>(1);
// Use context and data...
}
This allows passing dynamic data into jobs at runtime.
Scheduling Jobs
FluentScheduler provides many options for scheduling via its fluent API. Jobs can be scheduled to run:
- Once at a future date/time
- Repeatedly on a cron schedule
- Repeatedly at a fixed interval
- Based on calendar rules
Let’s look at some examples.
One-time Execution
To run a job once at a specific time in the future:
scheduler.Schedule<SampleJob>()
.ToRunOnceAt(someDateTime);
You can also run it once immediately:
scheduler.Schedule<SampleJob>().ToRunNow();
Fixed Interval Execution
To run a job on a fixed interval:
scheduler.Schedule<SampleJob>()
.ToRunEvery(15).Minutes();
Some other examples:
// Run every hour
.ToRunEvery(1).Hours();
// Run every day at midnight
.ToRunEvery(1).Days().At(0, 0);
// Run weekly
.ToRunEvery(1).Weeks().On(DayOfWeek.Sunday);
Cron Schedules
For more complex scheduling, you can use Cron expressions:
// Run Monday-Friday at 5 PM
scheduler.Schedule<SampleJob>()
.Cron("0 0 17 ? * MON-FRI")
// Run every 5 minutes
scheduler.Schedule<SampleJob>()
.Cron("0 */5 * ? * *")
This allows running jobs on complex calendar-based schedules.
Calendar Interval Schedules
FluentScheduler also provides a fluent API for calendar intervals:
scheduler.Schedule<SampleJob>()
.MonthlyOn(1) // Run monthly on the 1st day of the month
.On(DayOfWeek.Monday); // Run on Mondays
Some other examples:
// First weekday of month
.MonthlyOn(1, Weekday.Monday);
// Every 15th day of month
.EveryFifteenDays();
// Weekly on Tuesdays
.WeeklyOn(DayOfWeek.Tuesday);
This provides flexibility for scheduling based on calendar rules.
Custom Scheduling
In addition to the built-in scheduling options, you can create custom schedule definitions and implement the ISchedule
interface:
The scheduler will invoke GetNextRunTime()
to determine the next run date/time for the schedule.
Managing Jobs
Once jobs are scheduled, FluentScheduler provides ways to manage and monitor them:
Starting/Stopping
The scheduler can be started to begin running jobs:
scheduler.Start();
It can also be stopped:
scheduler.Stop();
This allows complete control over the scheduler lifecycle.
Monitoring
You can access a list of currently scheduled jobs:
var jobs = scheduler.GetScheduledJobs();
And see details about a particular job:
var job = scheduler.GetJobDetail(jobId);
var nextRun = job.NextRun;
var lastRun = job.LastRun;
if (job.IsCurrentlyExecuting)
{
// Job is running
}
Managing Failures
If jobs fail with exceptions, FluentScheduler will retry them up to a configured number of times. You can handle failures by subscribing to events:
scheduler.JobException += (sender, args) =>
{
var job = args.Job;
var exception = args.Exception;
// Handle job exception
}
You can also set a global error handler that will log or store information about failed jobs.
Priorities, Groups, and Concurrency
Jobs can be assigned a priority to control execution order. They can also be grouped logically and assigned concurrency limits.
For example:
scheduler.Schedule<CriticalJob>()
.WithHighPriority();
scheduler.Schedule<NonCriticalJob>()
.WithLowPriority();
scheduler.Schedule<ReportingJob>()
.WithGroup("reporting")
.PreventOverlapping("reporting");
This adds additional orchestration and organization capabilities.
Storing Job Data
By default, FluentScheduler stores the job state in memory. This means scheduled jobs will be lost when the application restarts.
For persisting job data, you can implement the IJobRegistry
interface and pass an instance to the scheduler on creation:
public class SqlJobRegistry : IJobRegistry
{
// Implements data access for jobs
}
var registry = new SqlJobRegistry();
var scheduler = new Scheduler(registry);
There are also built-in registry implementations for SQL Server, MongoDB, Redis, and more.
Conclusion
FluentScheduler makes it easy to create robust task schedulers in .NET applications. With its fluent API and abstractions over the underlying scheduler, you can focus on implementing job logic rather than complex cron schedules or concurrency controls. The library scales from simple recurring scripts to complex enterprise-grade distributed job workflows. It integrates well with .NET dependency injection, configuration, logging, and data access patterns.
If you need to add scheduled tasks to a .NET application, FluentScheduler is an excellent choice thanks to its simple but flexible API and lightweight implementation. The .NET Development Company can help you implement FluentScheduler effectively in your .NET projects.