Scheduling made easy
A Job Scheduler sitting on top of IHostedService
in .NET.
Often, one finds oneself between the simplicity of BackgroundService
/IHostedService
and the complexity of
a full-blown scheduler like Hangfire
or Quartz
.
This library aims to fill that gap by providing a simple and easy-to-use job scheduler that can be used in any .NET
application and feels "native".
There's no need to set up a database—just schedule your tasks right away! The library provides two ways of scheduling jobs:
The whole documentation can be found here: NCronJob Documentation
As this is a simple scheduler, some features are not included by design. If you need these features, you might want to
look into a more advanced scheduler like Hangfire
or Quartz
.
There are two ways to define a job.
You can use this library in a simple one-liner:
builder.Services.AddNCronJob((ILoggerFactory factory, TimeProvider timeProvider) =>
{
var logger = factory.CreateLogger("My Anonymous Job");
logger.LogInformation("Hello World - The current date and time is {Time}", timeProvider.GetLocalNow());
}, "*/5 * * * * *");
With this simple lambda, you can define a job that runs every 5 seconds. Pass in all dependencies, just like you would with a Minimal API.
IJob
interfaceusing NCronJob;
public class PrintHelloWorld : IJob
{
private readonly ILogger<PrintHelloWorld> logger;
public PrintHelloWorld(ILogger<PrintHelloWorld> logger)
{
this.logger = logger;
}
public Task RunAsync(IJobExecutionContext context, CancellationToken token)
{
logger.LogInformation("Hello World");
logger.LogInformation("Parameter: {Parameter}", context.Parameter);
return Task.CompletedTask;
}
}
Program.cs
builder.Services.AddNCronJob(options =>
options.AddJob<PrintHelloWorld>(j =>
{
// Every minute and optional parameter
j.WithCronExpression("* * * * *")
.WithParameter("Hello World");
}));
If the need arises and you want to trigger a job instantly, you can do so:
public class MyService
{
private readonly IInstantJobRegistry jobRegistry;
public MyService(IInstantJobRegistry jobRegistry) => this.jobRegistry = jobRegistry;
public void MyMethod() => jobRegistry.RunInstantJob<MyJob>("I am an optional parameter");
// Alternatively, you can also run an anonymous job
public void MyOtherMethod() => jobRegistry.RunInstantJob((MyOtherService service) => service.Do());
}
If you want a job to run when the application starts, you can configure it to run at startup using the RunAtStartup
method. Here is an example:
builder.Services.AddNCronJob(options =>
{
options.AddJob<MyJob>()
.RunAtStartup();
});
In this example, the job of type 'MyJob' will be executed as soon as the application starts. This is useful for tasks that need to run immediately upon application startup, such as initial data loading or cleanup tasks.
First you need to import data and then transform it? Well, but how do you make sure that the data is imported before you transform it? Sure, you could just give a delay, but what if the import takes longer than expected? This is where job dependencies come in handy!
builder.Services.AddNCronJob(options =>
{
options.AddJob<ImportData>(p => p.WithCronExpression("0 0 * * *")
.ExecuteWhen(
success: s => s.RunJob<TransformData>("Optional Parameter"),
faulted: s => s.RunJob<Notify>("Another Optional Parameter"));
});
You just want to trigger a service and don't want to define a whole new job? No problem! The Minimal API is available here as well:
builder.Services.AddNCronJob(options =>
{
options.AddJob<ImportData>(p => p.WithCronExpression("0 0 * * *")
.ExecuteWhen(
success: s => s.RunJob(async (ITransformer transformer) => await transformer.TransformDataAsync()),
faulted: s => s.RunJob(async (INotificationService notifier) => await notifier.NotifyAsync())
});
Thanks to all contributors and people who are creating bug reports and valuable input:
If you have any questions or suggestions, feel free to open a new issue or pull request. Do you want to contribute? Great! We have a predefined codespace for you to get started right away! With that you don't need any local setup and can start right away! Either coding or updating the documentation - everything is set and done for you!