FluentTaskScheduler is a lightweight and fluent-style job scheduling library for .NET. It allows you to define background jobs in a readable, chainable way and execute them inside your ASP.NET Core or Console host.
- Fluent, chainable API for defining jobs
- Works with Microsoft Dependency Injection (IServiceCollection)
- Runs as a background hosted service
- Supports interval-based or specific-time execution
- Supports excluded days and time ranges
- Exception-safe execution and logging
- No external dependencies
Install from NuGet:
dotnet add package FluentTaskScheduler --version 0.1.0-beta4
- CREATE YOUR SERVICE
Define an interface and a class containing the method that you want the scheduler to run.
public interface IMyService
{
Task DoWorkAsync();
}
public class MyService : IMyService
{
public async Task DoWorkAsync()
{
Console.WriteLine($"[{DateTime.Now:T}] Job executed.");
await Task.CompletedTask;
}
}In Program.cs (for .NET 6+ minimal hosting model):
using FluentTaskScheduler.DSL;
using FluentTaskScheduler.Extensions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
// Add the main scheduler service
services.AddFluentTaskScheduler();
// Register the scheduler for your interface
services.AddSchedulerFor<IMyService>();
// Register your actual service implementation
services.AddTransient<IMyService, MyService>();
})
.Build();Explanation:
- AddFluentTaskScheduler() registers the background runner (FlexibleSchedulerService).
- AddSchedulerFor() prepares a builder that can create jobs for your specific service.
- Your service (IMyService) is resolved from the dependency container at runtime.
- CREATE AND REGISTER YOUR JOB
After the host is built, obtain the scheduler builder and define your job.
var scheduler = host.Services.GetRequiredService<SchedulerBuilder<IMyService>>();
scheduler
.For(x => x.DoWorkAsync()) // which method to execute
.Every(TimeSpan.FromSeconds(30)) // how often to run
.Do(); // register itExplanation:
- For() specifies the method to call.
- Every() defines the repetition interval.
- Do() finalizes and registers the job inside the registry.
- RUN YOUR HOST
Finally start the host. The background service will begin running your jobs automatically.
Console.WriteLine("Scheduler started. Press Ctrl+C to stop.");
await host.RunAsync();When the program runs, you will see console messages every 30 seconds:
[10:15:00] Job executed.
[10:15:30] Job executed.Run every 5 minutes:
scheduler.For(x => x.DoWorkAsync())
.Every(TimeSpan.FromMinutes(5))
.Do();Run daily at 08:00 and 18:00:
scheduler.For(x => x.DoWorkAsync())
.DailyAt("08:00", "18:00")
.Do();Run every 10 minutes only between 08:00 and 18:00:
scheduler.For(x => x.DoWorkAsync())
.Every(TimeSpan.FromMinutes(10))
.Between(TimeSpan.FromHours(8), TimeSpan.FromHours(18))
.Do();Skip weekends:
scheduler.For(x => x.DoWorkAsync())
.Every(TimeSpan.FromHours(1))
.NotRunThisDays(DayOfWeek.Saturday, DayOfWeek.Sunday)
.Do();| Method | Description |
|---|---|
| For(Expression<Func<T, Task>>) | Specify the method to execute |
| Every(TimeSpan interval) | Repeats the job every given interval |
| DailyAt(params string[] times) | Runs at specific daily times |
| Between(TimeSpan start, TimeSpan end) | Restrict job to a time window |
| NotRunThisDays(params DayOfWeek[]) | Exclude specific days |
| Do() | Registers the job |
- The background executor (FlexibleSchedulerService) checks registered jobs every second.
- Jobs are executed on a ThreadPool thread (fire-and-forget).
- Each job keeps track of its own next execution time (NextRun).
- Only one instance of each job runs at a time (no overlapping).
- If both .Every(...) and .DailyAt(...) are used together, an exception is thrown.
- Excluding all seven days also throws an exception to prevent silent never-runs.
- Dependency Injection: Jobs can use any registered service type — transient, scoped, or singleton.
- Error Handling: Exceptions during execution are caught and logged via ILogger.
- Graceful Shutdown: The host’s cancellation token is passed down; the service will stop cleanly on Ctrl+C.
- AOT / Native Compilation: The library uses Expression.Compile() which may require trimming configuration for AOT builds.
- Cron expression support
- Persistent job storage (SQLite, Redis)
- Web dashboard for job monitoring
- Parallel and concurrency control
MIT License © Onur Köse https://github.com/Onur-Kose