Quartz.Extensions.DependencyInjection 提供与 Microsoft Dependency 依赖注入 的集成

提示
需要 Quartz 3.1 或更高版本。

安装

您需要将 NuGet 包引用添加到使用 Quartz 的项目中。

  1. Install-Package Quartz.Extensions.DependencyInjection

使用

您可以通过在 IServiceCollection 上调用扩展方法 AddQuartz 来添加 Quartz 配置。 配置构建使用强类型 API 包装各种配置属性。 您还可以使用配置部分 Quartz 中的标准 .NET Core appsettings.json 配置属性。

提示

Quartz.Extensions.Hosting 允许您为您的应用程序提供一个后台服务,用于处理启动和停止调度程序。

示例 appsettings.json

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Information",
  5. "Microsoft": "Warning",
  6. "Microsoft.Hosting.Lifetime": "Information"
  7. }
  8. },
  9. "Quartz": {
  10. "quartz.scheduler.instanceName": "Quartz ASP.NET Core Sample Scheduler"
  11. }
  12. }

DI 感知工作工厂

Quartz 带有两个内置的作业工厂替代方案,可以通过调用 UseMicrosoftDependencyInjectionJobFactoryUseMicrosoftDependencyInjectionScopedJobFactory(已弃用)进行配置。

提示

从 Quartz.NET 3.3.2 开始,默认作业工厂生成的所有作业都是作用域作业,您不应再使用 UseMicrosoftDependencyInjectionScopedJobFactory

作业实例构建

默认情况下,Quartz 将尝试从容器中解析作业的类型,如果没有显式注册,Quartz 将使用 ActivatorUtilities 构造作业并通过构造函数注入它的依赖项。 Job 应该只有一个公共构造函数。

持久性作业存储

每次您的应用程序启动并评估计划时,将根据数据库检查计划配置并相应更新。

警告

使用持久作业存储时,请确保为调度定义作业和触发器名称,以便存在性检查针对数据库中已有的数据正确工作。

使用 API 配置触发器和作业而没有明确的作业标识配置将导致作业和触发器在每次评估配置时生成不同的名称。

对于持久性作业存储,最好始终至少声明作业和触发器名称。 为它们省略组将为每次调用生成相同的默认组值。

示例 Startup.ConfigureServices 配置

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. // 来自 appsettings.json 的基本配置
  4. services.Configure<QuartzOptions>(Configuration.GetSection("Quartz"));
  5. // 如果您使用的是持久作业存储,您可能需要更改一些选项
  6. services.Configure<QuartzOptions>(options =>
  7. {
  8. options.Scheduling.IgnoreDuplicates = true; // 默认值:false
  9. options.Scheduling.OverWriteExistingData = true; // 默认值:true
  10. });
  11. services.AddQuartz(q =>
  12. {
  13. // 当集群的一部分或您想以其他方式识别多个调度程序时很方便
  14. q.SchedulerId = "Scheduler-Core";
  15. // 我们从 appsettings.json 中获取它,
  16. // 只是表明它是可能的 q.SchedulerName = "Quartz ASP.NET Core Sample Scheduler";
  17. // 从 3.3.2 开始,这也可以毫无问题地注入作用域服务(如 EF DbContext)
  18. q.UseMicrosoftDependencyInjectionJobFactory();
  19. // 或用于范围服务支持,例如 EF Core DbContext
  20. // q.UseMicrosoftDependencyInjectionScopedJobFactory();
  21. // 这些是默认值
  22. q.UseSimpleTypeLoader();
  23. q.UseInMemoryStore();
  24. q.UseDefaultThreadPool(tp =>
  25. {
  26. tp.MaxConcurrency = 10;
  27. });
  28. // 使用单个触发器创建作业的最快方法是使用 ScheduleJob
  29. // (需要 3.2 版)
  30. q.ScheduleJob<ExampleJob>(trigger => trigger
  31. .WithIdentity("Combined Configuration Trigger")
  32. .StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(7)))
  33. .WithDailyTimeIntervalSchedule(x => x.WithInterval(10, IntervalUnit.Second))
  34. .WithDescription("my awesome trigger configured for a job with single call")
  35. );
  36. // 您还可以使用代码配置单个作业和触发器,这允许您将多个触发器与同一个作业相关联
  37. // (例如,如果您希望每个触发器有不同的作业数据映射)
  38. q.AddJob<ExampleJob>(j => j
  39. .StoreDurably() // 如果没有关联触发器,我们需要持久存储
  40. .WithDescription("my awesome job")
  41. );
  42. // 这是触发器的已知工作
  43. var jobKey = new JobKey("awesome job", "awesome group");
  44. q.AddJob<ExampleJob>(jobKey, j => j
  45. .WithDescription("my awesome job")
  46. );
  47. q.AddTrigger(t => t
  48. .WithIdentity("Simple Trigger")
  49. .ForJob(jobKey)
  50. .StartNow()
  51. .WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromSeconds(10)).RepeatForever())
  52. .WithDescription("my awesome simple trigger")
  53. );
  54. q.AddTrigger(t => t
  55. .WithIdentity("Cron Trigger")
  56. .ForJob(jobKey)
  57. .StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(3)))
  58. .WithCronSchedule("0/3 * * * * ?")
  59. .WithDescription("my awesome cron trigger")
  60. );
  61. // 您也可以添加日历(需要 3.2 版)
  62. const string calendarName = "myHolidayCalendar";
  63. q.AddCalendar<HolidayCalendar>(
  64. name: calendarName,
  65. replace: true,
  66. updateTriggers: true,
  67. x => x.AddExcludedDate(new DateTime(2020, 5, 15))
  68. );
  69. q.AddTrigger(t => t
  70. .WithIdentity("Daily Trigger")
  71. .ForJob(jobKey)
  72. .StartAt(DateBuilder.EvenSecondDate(DateTimeOffset.UtcNow.AddSeconds(5)))
  73. .WithDailyTimeIntervalSchedule(x => x.WithInterval(10, IntervalUnit.Second))
  74. .WithDescription("my awesome daily time interval trigger")
  75. .ModifiedByCalendar(calendarName)
  76. );
  77. // 还可以添加 XML 配置并轮询它以进行更改
  78. q.UseXmlSchedulingConfiguration(x =>
  79. {
  80. x.Files = new[] { "~/quartz_jobs.config" };
  81. x.ScanInterval = TimeSpan.FromSeconds(2);
  82. x.FailOnFileNotFound = true;
  83. x.FailOnSchedulingError = true;
  84. });
  85. // 使用可以处理 Windows/Linux 差异的转换器转换时区
  86. q.UseTimeZoneConverter();
  87. // 自动中断长时间运行的作业
  88. q.UseJobAutoInterrupt(options =>
  89. {
  90. // 这是默认的
  91. options.DefaultMaxRunTime = TimeSpan.FromMinutes(5);
  92. });
  93. q.ScheduleJob<SlowJob>(
  94. triggerConfigurator => triggerConfigurator
  95. .WithIdentity("slowJobTrigger")
  96. .StartNow()
  97. .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever()),
  98. jobConfigurator => jobConfigurator
  99. .WithIdentity("slowJob")
  100. .UsingJobData(JobInterruptMonitorPlugin.JobDataMapKeyAutoInterruptable, true)
  101. // 此作业只允许五秒钟,覆盖默认配置
  102. .UsingJobData(JobInterruptMonitorPlugin.JobDataMapKeyMaxRunTime, TimeSpan.FromSeconds(5).TotalMilliseconds.ToString(CultureInfo.InvariantCulture)));
  103. // 添加一些监听器
  104. q.AddSchedulerListener<SampleSchedulerListener>();
  105. q.AddJobListener<SampleJobListener>(GroupMatcher<JobKey>.GroupEquals(jobKey.Group));
  106. q.AddTriggerListener<SampleTriggerListener>();
  107. // 以 JSON 序列化器为例的持久作业存储示例
  108. /*
  109. q.UsePersistentStore(s =>
  110. {
  111. s.UseProperties = true;
  112. s.RetryInterval = TimeSpan.FromSeconds(15);
  113. s.UseSqlServer(sqlServer =>
  114. {
  115. sqlServer.ConnectionString = "some connection string";
  116. // 这是默认的
  117. sqlServer.TablePrefix = "QRTZ_";
  118. });
  119. s.UseJsonSerializer();
  120. s.UseClustering(c =>
  121. {
  122. c.CheckinMisfireThreshold = TimeSpan.FromSeconds(20);
  123. c.CheckinInterval = TimeSpan.FromSeconds(10);
  124. });
  125. });
  126. */
  127. });
  128. // 我们可以使用选项模式来支持挂接您自己的配置,
  129. // 因为我们不使用服务注册 api,
  130. // 我们需要手动确保作业存在于 DI 中
  131. services.AddTransient<ExampleJob>();
  132. services.Configure<SampleOptions>(Configuration.GetSection("Sample"));
  133. services.AddOptions<QuartzOptions>()
  134. .Configure<IOptions<SampleOptions>>((options, dep) =>
  135. {
  136. if (!string.IsNullOrWhiteSpace(dep.Value.CronSchedule))
  137. {
  138. var jobKey = new JobKey("options-custom-job", "custom");
  139. options.AddJob<ExampleJob>(j => j.WithIdentity(jobKey));
  140. options.AddTrigger(trigger => trigger
  141. .WithIdentity("options-custom-trigger", "custom")
  142. .ForJob(jobKey)
  143. .WithCronSchedule(dep.Value.CronSchedule));
  144. }
  145. });
  146. // Quartz.Extensions.Hosting 允许您触发处理调度程序生命周期的后台服务
  147. services.AddQuartzHostedService(options =>
  148. {
  149. // 关闭时,我们希望作业优雅地完成
  150. options.WaitForJobsToComplete = true;
  151. });
  152. }