在 .NET中使用Cronos实现计划任务功能
概述:Cronos 是 .NET 的任务计划库,允许使用 CRON 模式在特定时间或间隔计划和执行任务。在本文中,我将介绍如何在 .NET 8 应用程序的后台设置和使用任务计划。先决条件.NET 8.0Visual Studio 2022Nuget 包 CronosWeb API在API的Program.cs类中,配置如下图所示:using Sample.Scheduler.Core.Extensions;using Sample.Scheduler.Core.TimerSchedulers;var builder = WebApplication.CreateBuilder(args);build
Cronos 是 .NET 的任务计划库,允许使用 CRON 模式在特定时间或间隔计划和执行任务。
在本文中,我将介绍如何在 .NET 8 应用程序的后台设置和使用任务计划。
先决条件
- .NET 8.0
- Visual Studio 2022
- Nuget 包 Cronos
Web API
在API的Program.cs类中,配置如下图所示:
using Sample.Scheduler.Core.Extensions;
using Sample.Scheduler.Core.TimerSchedulers;
var builder = WebApplication.CreateBuilder(args);
builder.AddSerilog();
builder.Services.AddRouting(options => options.LowercaseUrls = true);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddCronJob<TimerSendEmail>(c => c.CronExpression = @"0 */1 * * * *");
builder.Services.AddCronJob<TimerCheckDatabase>(c => c.CronExpression = @"* * * * * *");
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.MapControllers();
app.Run();
下一步是创建扩展类 CronJobExtensions,负责使用 CronExpression 控制每个任务的下一个计划:
public abstract class CronJobExtensions : BackgroundService
{
private readonly CronExpression _expression;
private readonly TimeZoneInfo _timeZoneInfo;
private readonly IServiceProvider _serviceProvider;
protected CronJobExtensions(string cronExpression, TimeZoneInfo timeZoneInfo, IServiceProvider serviceProvider)
{
_expression = CronExpression.Parse(cronExpression, CronFormat.IncludeSeconds);
_timeZoneInfo = timeZoneInfo;
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var now = DateTimeOffset.Now;
var next = _expression.GetNextOccurrence(now, _timeZoneInfo);
if (!next.HasValue) continue;
var delay = next.Value - now;
await Task.Delay(delay, cancellationToken);
if (cancellationToken.IsCancellationRequested) continue;
try
{
using var scope = _serviceProvider.CreateScope();
await DoWork(scope, cancellationToken);
}
catch (Exception ex)
{
Log.Error(ex, nameof(ExecuteAsync));
}
}
}
public abstract Task DoWork(IServiceScope scope, CancellationToken cancellationToken);
}
public interface IScheduleConfig<T>
{
string CronExpression { get; set; }
TimeZoneInfo TimeZoneInfo { get; set; }
}
public class ScheduleConfig<T> : IScheduleConfig<T>
{
public string CronExpression { get; set; }
public TimeZoneInfo TimeZoneInfo { get; set; } = TimeZoneInfo.Local;
}
接下来,必须创建负责注册计划的扩展类 ScheduledServiceExtensions:
public static class ScheduledServiceExtensions
{
public static IServiceCollection AddCronJob<T>(this IServiceCollection services, Action<IScheduleConfig<T>> options) where T : CronJobExtensions
{
var config = new ScheduleConfig<T>();
options.Invoke(config);
services.AddSingleton<IScheduleConfig<T>>(config);
services.AddHostedService<T>();
return services;
}
}
最后,让我们创建用于调度操作的类,在此示例中,我们将创建一个每秒触发一次的计划,每分钟触发一次。这两个时间表在Program.cs类中注册
public class TimerCheckDatabase : CronJobExtensions
{
public TimerCheckDatabase(IScheduleConfig<TimerCheckDatabase> config, IServiceProvider serviceProvider)
: base(config.CronExpression, config.TimeZoneInfo, serviceProvider)
{
}
public override Task DoWork(IServiceScope scope, CancellationToken cancellationToken)
{
Serilog.Log.Information("Verified Database!");
return Task.CompletedTask;
}
}
public class TimerSendEmail : CronJobExtensions
{
public TimerSendEmail(IScheduleConfig<TimerSendEmail> config, IServiceProvider serviceProvider)
: base(config.CronExpression, config.TimeZoneInfo, serviceProvider)
{
}
public override Task DoWork(IServiceScope scope, CancellationToken cancellationToken)
{
Serilog.Log.Information("Email sent!");
return Task.CompletedTask;
}
}
测试
要测试计划,请运行 API 并在应用程序控制台中分析 Serilog 生成的日志:
可以看出,检查数据库的任务计划每秒触发一次,而发送电子邮件的计划每分钟触发一次。
Cronos 是一个用于解析 Cron 表达式和计算下一次出现次数的库,有了它,可以安排重要的后台任务,例如:每天早上执行数据库维护、不时从源捕获数据、创建警报等。