目錄
介紹
Quartz.NET是一個方便的庫,允許您通過實現IJob接口來安排重復任務。然而,它的局限性在於,默認情況下,它僅支持無參數構造函數,這使得在其內部注入外部服務變得復雜,即,用於實現存儲庫模式。在本文中,我們將了解如何使用標准.NET Core DI容器解決此問題。
本文中提到的整個項目在以下Github存儲庫中提供。為了更好地遵循文章中的代碼,您可能需要查看它。
項目概況
我們來看看最初的解決方案結構。
項目QuartzDI.Demo.External.DemoService代表了一些我們無法控制的外部依賴。為了簡單起見,它的工作非常簡單。
項目QuartzDI.Demo是我們的工作項目,包含簡單的Quartz.NET作業。
1 public class DemoJob : IJob 2 { 3 private const string Url = "https://i.ua"; 4 5 public static IDemoService DemoService { get; set; } 6 7 public Task Execute(IJobExecutionContext context) 8 { 9 DemoService.DoTask(Url); 10 return Task.CompletedTask; 11 } 12 }
這是以直接的方式設置的:
1 var props = new NameValueCollection 2 { 3 { "quartz.serializer.type", "binary" } 4 }; 5 var factory = new StdSchedulerFactory(props); 6 var sched = await factory.GetScheduler(); 7 await sched.Start(); 8 var job = JobBuilder.Create<DemoJob>() 9 .WithIdentity("myJob", "group1") 10 .Build(); 11 var trigger = TriggerBuilder.Create() 12 .WithIdentity("myTrigger", "group1") 13 .StartNow() 14 .WithSimpleSchedule(x => x 15 .WithIntervalInSeconds(5) 16 .RepeatForever()) 17 .Build(); 18 await sched.ScheduleJob(job, trigger);
我們通過作業的static屬性提供外部服務
1 DemoJob.DemoService = new DemoService();
由於該項目是一個控制台應用程序,在本文的課程中,我們必須手動安裝所有需要的基礎架構,並能夠更全面地了解.NET Core實際上為我們帶來了什么。
此時,我們的項目正在運行。最重要的是它很簡單,很棒。但是,如果我們想把它作為一個小工具,那么我們就會為這種簡單性付出代價。但這通常不是生產系統的情況。所以讓我們稍微調整一下以使其更靈活。
創建配置文件
其中一個缺點是我們硬編碼調用到DemoJob中的URL 。理想情況下,我們希望更改它,並根據我們的環境進行更改。.NET Core附帶了appsettings.json機制。
為了開始使用.NET Core配置機制,我們必須安裝幾個Nuget包:
1 Microsoft.Extensions.Configuration 2 Microsoft.Extensions.Configuration.FileExtensions 3 Microsoft.Extensions.Configuration.Json
讓我們創建一個具有這樣名稱的文件並在那里提取我們的URL:
1 { 2 "connection": { 3 "Url": "http://i.ua" 4 } 5 }
現在我們可以從配置文件中提取我們的值,如下所示:
1 var builder = new ConfigurationBuilder() 2 .SetBasePath(Directory.GetCurrentDirectory()) 3 .AddJsonFile("appsettings.json", true, true); 4 var configuration = builder.Build(); 5 var connectionSection = configuration.GetSection("connection"); 6 DemoJob.Url = connectionSection["Url"];
請注意,要實現它,我們必須將Url從常量更改為屬性。
1 public static string Url { get; set; }
使用構造函數注入
通過static屬性注入服務對於一個簡單的項目來說很好,但是對於一個更大的項目,它可能帶來一些缺點:例如可能在沒有服務的情況下調用作業,因此失敗或在對象運行時期間改變依賴性,這使得更難以推理對象。要解決這些問題,我們應該使用構造函數注入。
雖然純依賴注入沒有任何問題,但是有些人認為你應該在本文中努力實現它,我們將使用Nuget包附帶的內置.NET Core DI容器Microsoft.Extensions.DependencyInjection。
現在我們指定我們依賴於構造函數參數的服務:
1 private readonly IDemoService _demoService; 2 3 public DemoJob(IDemoService demoService) 4 { 5 _demoService = demoService; 6 }
為了調用作業的參數構造函數,Quartz.NET提供了IJobFactory接口。這是我們的實現:
1 public class DemoJobFactory : IJobFactory 2 { 3 private readonly IServiceProvider _serviceProvider; 4 5 public DemoJobFactory(IServiceProvider serviceProvider) 6 { 7 _serviceProvider = serviceProvider; 8 } 9 10 public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) 11 { 12 return _serviceProvider.GetService<DemoJob>(); 13 } 14 15 public void ReturnJob(IJob job) 16 { 17 var disposable = job as IDisposable; 18 disposable?.Dispose(); 19 } 20 }
讓我們注冊我們的依賴項:
1 var serviceCollection = new ServiceCollection(); 2 serviceCollection.AddScoped<DemoJob>(); 3 serviceCollection.AddScoped<IDemoService, DemoService>(); 4 var serviceProvider = serviceCollection.BuildServiceProvider();
拼圖的最后一塊是讓Quartz.NET使用我們的工廠。IScheduler有屬性JobFactory只是為了這件事。
1 sched.JobFactory = new DemoJobFactory(serviceProvider);
使用選項模式
現在我們可以使用配置選項來實現相同的技巧。同樣,我們的例子以Nuget包開始。這一次是Microsoft.Extensions.Options。
讓我們為配置選項創建一個強類型定義:
1 public class DemoJobOptions 2 { 3 public string Url { get; set; } 4 }
現在我們按如下方式填充它們:
serviceCollection.AddOptions(); serviceCollection.Configure<DemoJobOptions>(options => { options.Url = connectionSection["Url"]; });
並將它們注入構造函數中。不是我們直接注入IOptions<T>,不是options實例。
1 public DemoJob(IDemoService demoService, IOptions<DemoJobOptions> options) 2 { 3 _demoService = demoService; 4 _options = options.Value; 5 }
結論
在本文中,我們已經了解了如何利用.NET Core功能使Quartz.NET的使用更加靈活。