需要使用 Quartz .NET
csharp
<PackageReference Include="Quartz.AspNetCore"/>
建立 Global JobKey
csharp
public static class GlobalJobKey
{
public static JobKey CustomizedKey => new JobKey("xxx scheduled", "group1");
}
建立 Job
csharp
public class CustomizedJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
// pass the parameter if you want,
// depends on your business logic
var customizedData =
JsonSerializer.Deserialize<YourType>(context.MergedJobDataMap.Get("data").ToString()!);
// your logic
}
}
在 Program.cs 中配置 Quartz .NET
csharp
// construct the Quartz Trigger
builder.Services.AddQuartz(q =>
{
q.AddJob<CustomizedJob>(opt => { opt.WithIdentity(GlobalJobKey.CustomizedKey).StoreDurably(); });
});
builder.Services.AddQuartzHostedService(options => { options.WaitForJobsToComplete = true; });
builder.Services.AddScoped<IJob, CustomizedJob>();
可以在任意地方: Controller, Background Service, Service... 中进行配置
csharp
public class CustomizedBackgroundService(
ISchedulerFactory factory,
IServiceScopeFactory scopeFactory,
ILogger<CustomizedBackgroundService> logger) : IHostedService
{
private IScheduler? Schedule { get; set; }
public async Task StartAsync(CancellationToken cancellationToken)
{
if (Schedule != null)
{
await StopJobs(cancellationToken);
}
using var scope = scopeFactory.CreateScope();
var repo = scope.ServiceProvider.GetRequiredService<CustomizedRepository>();
var configurations = (await repo.Query()).ToArray();
Schedule = await factory.GetScheduler(cancellationToken);
await Schedule.Start(cancellationToken);
var jobDetail = JobBuilder.Create<CustomizedJob>()
.WithIdentity(GlobalJobKey.CustomizedKey)
.StoreDurably()
.Build();
await Schedule.AddJob(jobDetail, true, cancellationToken);
Array.ForEach(configurations, Action);
return;
async void Action(CustomizedConfiguration configuration)
{
var triggerKey = new TriggerKey($"{configuration.Name}-trigger", "group1");
var existingTrigger = await Schedule.GetTrigger(triggerKey, cancellationToken);
if (existingTrigger != null)
await Schedule.UnscheduleJob(triggerKey, cancellationToken);
var trigger = TriggerBuilder.Create()
.WithIdentity(triggerKey)
.ForJob(GlobalJobKey.CustomizedKey)
.WithCronSchedule("0 0 0 ? * 1")
.UsingJobData("data", JsonSerializer.Serialize(configuration))
.StartNow()
.Build();
await Schedule.ScheduleJob(trigger, cancellationToken);
logger.LogCritical("add trigger for: {Trigger}", $"{configuration.Name}-trigger");
}
}
public async Task StopAsync(CancellationToken cancellationToken)
{
if (Schedule != null)
{
await StopJobs(cancellationToken);
}
}
private async Task StopJobs(CancellationToken cancellationToken)
{
await Schedule?.Shutdown(cancellationToken)!;
Schedule?.Clear(cancellationToken);
}
}