This guide demonstrates how to integrate MiniCron.Core into a minimal .NET Console application.
- .NET 8.0 or later
- Basic knowledge of C# and dependency injection
dotnet new console -n MiniCronConsoleApp
cd MiniCronConsoleAppdotnet add package MiniCron.Core
dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Extensions.LoggingHere's a complete minimal example:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MiniCron.Core.Extensions;
var builder = Host.CreateApplicationBuilder(args);
// Configure logging
builder.Logging.AddConsole();
// Register MiniCron with scheduled jobs
builder.Services.AddMiniCron(options =>
{
// Run every minute
options.AddJob("* * * * *", async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Job executed at {Time}", DateTime.Now);
await Task.CompletedTask;
});
// Run every 5 minutes
options.AddJob("*/5 * * * *", async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("5-minute job executed at {Time}", DateTime.Now);
await Task.CompletedTask;
});
});
var app = builder.Build();
await app.RunAsync();If you prefer the registry-style initializer (also supported), you can register jobs using the ergonomic overloads and subscribe to lifecycle events:
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMiniCron(registry =>
{
// Subscribe to registry events
registry.JobAdded += (s, e) => Console.WriteLine($"Job added: {e.Job.Id} {e.Job.CronExpression}");
registry.JobRemoved += (s, e) => Console.WriteLine($"Job removed: {e.Job.Id}");
registry.JobUpdated += (s, e) => Console.WriteLine($"Job updated: {e.Job.Id} {e.PreviousJob?.CronExpression} -> {e.Job.CronExpression}");
// Use overload that accepts CancellationToken-aware delegate
registry.ScheduleJob("*/5 * * * *", async (ct) =>
{
Console.WriteLine("Running token-aware job every 5 minutes");
await Task.CompletedTask;
});
// Use simple synchronous Action overload
registry.ScheduleJob("* * * * *", () => Console.WriteLine("Simple action every minute"));
// Legacy-style delegate that receives IServiceProvider
registry.ScheduleJob("0 * * * *", (sp, ct) =>
{
var logger = sp.GetService<ILogger<Program>>();
if (logger is not null)
{
logger.LogInformation("Hourly job executed");
}
return Task.CompletedTask;
});
});
var app = builder.Build();
await app.RunAsync();This short example shows configuring scheduler behavior via AddMiniCronOptions(...) and resolving IOptions<MiniCronOptions> inside a scheduled job.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MiniCron.Core.Extensions;
using MiniCron.Core.Models;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole();
// Configure scheduler defaults (timezone, concurrency, timeouts)
builder.Services.AddMiniCronOptions(opts =>
{
opts.TimeZone = TimeZoneInfo.Utc;
opts.MaxConcurrency = 5;
opts.DefaultJobTimeout = TimeSpan.FromMinutes(2);
});
var app = builder.Build();
// Register runtime jobs using the JobRegistry singleton
var registry = app.Services.GetRequiredService<JobRegistry>();
registry.ScheduleJob("* * * * *", (serviceProvider, cancellationToken) =>
{
// Resolve configured options inside the job
var configured = serviceProvider.GetRequiredService<IOptions<MiniCronOptions>>().Value;
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Running job (TZ={TZ}, Max={Max})", configured.TimeZone, configured.MaxConcurrency);
return Task.CompletedTask;
});
await app.RunAsync();dotnet runYou should see output like:
info: Program[0]
Job executed at 12/18/2025 11:25:00 AM
info: Program[0]
5-minute job executed at 12/18/2025 11:25:00 AM
info: Program[0]
Job executed at 12/18/2025 11:26:00 AM
public interface INotificationService
{
Task SendNotificationAsync(string message, CancellationToken cancellationToken);
}
public class ConsoleNotificationService : INotificationService
{
private readonly ILogger<ConsoleNotificationService> _logger;
public ConsoleNotificationService(ILogger<ConsoleNotificationService> logger)
{
_logger = logger;
}
public async Task SendNotificationAsync(string message, CancellationToken cancellationToken)
{
_logger.LogInformation("Notification: {Message}", message);
await Task.Delay(100, cancellationToken); // Simulate async work
}
}using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MiniCron.Core.Extensions;
var builder = Host.CreateApplicationBuilder(args);
// Configure logging
builder.Logging.AddConsole();
// Register custom services
builder.Services.AddSingleton<INotificationService, ConsoleNotificationService>();
// Register MiniCron with scheduled jobs
builder.Services.AddMiniCron(options =>
{
// Daily notification at 9:00 AM
options.AddJob("0 9 * * *", async (serviceProvider, cancellationToken) =>
{
var notificationService = serviceProvider.GetRequiredService<INotificationService>();
await notificationService.SendNotificationAsync("Good morning! Daily task started.", cancellationToken);
});
// Hourly status check
options.AddJob("0 * * * *", async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Hourly status check completed");
await Task.CompletedTask;
});
});
var app = builder.Build();
await app.RunAsync();You can also load cron expressions from configuration:
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"CronJobs": {
"DailyReport": "0 9 * * *",
"HourlyCheck": "0 * * * *",
"EveryMinute": "* * * * *"
}
}using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MiniCron.Core.Extensions;
var builder = Host.CreateApplicationBuilder(args);
// Configure logging
builder.Logging.AddConsole();
// Register MiniCron with scheduled jobs from configuration
builder.Services.AddMiniCron(options =>
{
var config = builder.Configuration;
// Daily report
var dailyReportCron = config["CronJobs:DailyReport"] ?? "0 9 * * *";
options.AddJob(dailyReportCron, async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Generating daily report...");
await Task.CompletedTask;
});
// Hourly check
var hourlyCheckCron = config["CronJobs:HourlyCheck"] ?? "0 * * * *";
options.AddJob(hourlyCheckCron, async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Performing hourly check...");
await Task.CompletedTask;
});
});
var app = builder.Build();
await app.RunAsync();For testing purposes, you can use frequent cron expressions. Note that standard cron expressions support minutes as the smallest time unit:
// Test job that runs every minute (smallest interval supported)
options.AddJob("* * * * *", async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Test job executed");
await Task.CompletedTask;
});
// If you need sub-minute testing during development,
// consider using a test framework or mock time| Expression | Description |
|---|---|
* * * * * |
Every minute |
*/5 * * * * |
Every 5 minutes |
0 * * * * |
Every hour |
0 */6 * * * |
Every 6 hours |
0 0 * * * |
Daily at midnight |
0 9 * * 1-5 |
Weekdays at 9 AM |
0 2 * * 0 |
Every Sunday at 2 AM |
- Use Dependency Injection: Always resolve services from the
serviceProviderparameter - Handle Cancellation: Respect the
cancellationTokenin your async operations - Logging: Use structured logging for better diagnostics
- Error Handling: Wrap job logic in try-catch blocks for resilience
- Keep Jobs Small: Break complex tasks into smaller, focused jobs
- Configuration: Store cron expressions in configuration files for flexibility
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MiniCron.Core.Extensions;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole();
builder.Services.AddMiniCron(options =>
{
// Simple job
options.AddJob("* * * * *", async (serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
try
{
logger.LogInformation("Starting scheduled task at {Time}", DateTime.Now);
// Your business logic here
await Task.Delay(1000, cancellationToken);
logger.LogInformation("Scheduled task completed successfully");
}
catch (OperationCanceledException)
{
logger.LogWarning("Task was cancelled");
}
catch (Exception ex)
{
logger.LogError(ex, "Error executing scheduled task");
}
});
});
var app = builder.Build();
await app.RunAsync();