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:
- Instant jobs - Run a job immediately (or with a small delay, or at a specific date and time).
- Cron jobs - Schedule a job using a cron expression.
The whole documentation can be found here: NCronJob Documentation
If you are new to the project, start with:
-
llms.txtfor machine-friendly discovery
- The ability to schedule jobs using a cron expression.
- The ability to instantly run a job.
- Parameterized jobs - Instant as well as cron jobs!
- Integration with ASP.NET - Access your DI container like you would in any other service.
- Get notified when a job is done (either successfully or with an error).
- Retries - If a job fails, it will be retried.
- The job scheduler supports TimeZones. Defaults to UTC time.
- Minimal API for Jobs - Implement jobs in a one-liner.
- Startup jobs - Run a job when the application starts.
- Define job dependencies - trigger another job if one was successful or faulted!
- Add, remove, and update jobs at runtime.
- Observe the progress of a job's execution.
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.
- Job persistence - Jobs are not persisted between restarts of the application.
- Job history - There is no history of jobs that have been run.
- Use the Minimal Job API when you want the smallest setup and delegate-based jobs
- Use
IJobimplementations when you want reusable job types, notification handlers, or richer orchestration - Use named jobs when you need runtime management or multiple registrations of the same job type
- Use startup jobs when work must run during application startup
Working samples live in:
For the generic-host examples below, use an ASP.NET app, a worker service, or another project that already references Microsoft.Extensions.Hosting.
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 * * * * *");
await builder.Build().RunAsync();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.
- Import the namespace (or let your IDE do the dirty work)
using NCronJob;- Create a job
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;
}
}- Register the NCronJob and the job in your
Program.cs
builder.Services.AddNCronJob(options =>
options.AddJob<PrintHelloWorld>(j =>
{
// Every minute and optional parameter
j.WithCronExpression("* * * * *")
.WithParameter("Hello World")
.WithTimeout(TimeSpan.FromMinutes(2))
.WithJobRunExpiry(TimeSpan.FromMinutes(5));
}));Scheduler-wide concurrency and queued-run expiry can be configured on the outer builder. By default, concurrency is
Environment.ProcessorCount * 4, job execution has no timeout, and queued runs expire after ten minutes.
Timeout.InfiniteTimeSpan disables either timeout or expiry.
builder.Services.AddNCronJob(options => options
.WithMaxDegreeOfParallelism(16)
.WithDefaultJobRunExpiry(TimeSpan.FromMinutes(15))
.AddJob<PrintHelloWorld>());- Run your application and see the magic happen!
Call UseNCronJobAsync() or UseNCronJob() when you register startup jobs via RunAtStartup(...).
using Microsoft.Extensions.Logging;
using NCronJob;
var builder = Microsoft.AspNetCore.Builder.WebApplication.CreateBuilder(args);
builder.Services.AddNCronJob(options =>
{
options.AddJob<MyJob>(j => j.RunAtStartup());
});
var app = builder.Build();
await app.UseNCronJobAsync();
await app.RunAsync();
public sealed class MyJob(ILogger<MyJob> logger) : NCronJob.IJob
{
public Task RunAsync(NCronJob.IJobExecutionContext context, CancellationToken token)
{
logger.LogInformation("Startup job executed.");
return Task.CompletedTask;
}
}Regular recurring jobs and instant jobs do not require this call.
Detailed feature docs:
- Getting Started
- Define and Schedule Jobs
- Triggering instant jobs
- Model Dependencies
- Dynamic Job Control
- Known gotchas
Sample applications:
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());
}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!