1. 程式人生 > >Topshelf+Quartz在.Net Core框架下的實現

Topshelf+Quartz在.Net Core框架下的實現

  在我們日常開發工作中,經常會運用到Quartz+Topshelf元件的組合來開發一些定時任務。那麼在.Net Core下如何去使用呢?我自己嘗試搭建了一個測試專案,過程中遇到了以下一些問題:

  • Quartz 配置檔案及版本問題。我們知道Quartz有2個配置檔案,quartz.config和quartz.job.xml。前者負責元件初始化配置,後者負責job和triggle的配置。剛開始我是直接把framework下的配置檔案直接拿過來用的,啟動直接報錯。主要問題在quartz.config
    quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz
    

    這一段上。原因Quartz最新版本已經將Plugin模組單獨剝離出一個獨立的DLL,這裡的引用也要變化。需要Nuget上下載Quartz.Plugins元件,並將上一段改成

    quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz.Plugins
    

     

  • DI問題。為了貼合.Net Core DI精神,我們也要來實現Console程式的DI功能。第一個問題是Job如何DI?首先我們需要自己去實現JobFactory

        public class NewJobFactory : IJobFactory
        {
            private readonly IServiceProvider _serviceProvider;
    
            public NewJobFactory(IServiceProvider serviceProvider)
            {
                _serviceProvider = serviceProvider;
            }
            public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
            {
                return _serviceProvider.GetService(bundle.JobDetail.JobType) as IJob;
            }
    
            public void ReturnJob(IJob job)
            {
                var disposable = job as IDisposable;
                disposable?.Dispose();
            }
        }
    

      注入方式

    IServiceCollection services = new ServiceCollection();
    services.AddScoped<IJobFactory, NewJobFactory>();
    services.AddSingleton(service =>
    {
         var scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
         scheduler.JobFactory = service.GetService<IJobFactory>();
         return scheduler;
    });

     

  • Console程式的配置檔案獲取以及注入問題。眾所周知,.Net Core下建立的Console程式就是一塊白板,什麼都沒有。配置檔案我們還得自己去建一個.json檔案。並且需要自己從Nuget上下載的元件包(見後面專案結構截圖)。載入方式如下
  •         private IConfiguration ConfigureConfiguration()
            {            
                //配置檔案
                var builder = new ConfigurationBuilder()
                    .SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
                return builder.Build();
            }
    

      注入方式如下

                if (configuration != null)
                {
                    //iconfiguration注入
                    services.AddSingleton<IConfiguration>(configuration);
                }
    
                //自定義 option方式注入
                services.Configure<AppSetting>(configuration);
    

      這樣就可以在後續的類程式碼中 直接通過DI方式獲取IConfiguration物件或者你自己的Option物件了

  貼上完整程式碼。

  Program.cs

        static void Main(string[] args)
        {
            HostFactory.Run(x =>
            {
                x.Service<ServiceRunner>();
                x.SetDescription("gt.dotnetcore.consolesample");
                x.SetDisplayName("gt.dotnetcore.consolesample");
                x.SetServiceName("gt.dotnetcore.consolesample");

                x.StartAutomatically();
            });
        }

  ServiceRunner.cs 啟動的核心

    public class ServiceRunner : ServiceControl
    {
        //private readonly IScheduler _scheduler;

        private IServiceProvider _serviceProvider;
        public ServiceRunner()
        {
            var configurationRoot = ConfigureConfiguration();
            _serviceProvider = ConfigureServices(configurationRoot);
        }

        private IConfiguration ConfigureConfiguration()
        {            
            //配置檔案
            var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
            return builder.Build();
        }
        private IServiceProvider ConfigureServices(IConfiguration configuration)
        {
            //依賴注入
            IServiceCollection services = new ServiceCollection();
            //後續需要使用log的話,這裡需要注入
            services.AddTransient<ILoggerFactory, LoggerFactory>();
            services.AddTransient<ITest, TestBiz>();
            services.AddScoped<IJobFactory, NewJobFactory>();
            if (configuration != null)
            {
                //iconfiguration注入
                services.AddSingleton<IConfiguration>(configuration);
            }

            //自定義 option方式注入
            services.Configure<AppSetting>(configuration);
            //這裡注意Job的注入方式,不要強制指定IJob實現方式!!
            services.AddScoped<TestJob>();
            services.AddScoped<Test2Job>();
            services.AddSingleton(service =>
            {
                var scheduler = StdSchedulerFactory.GetDefaultScheduler().Result;
                scheduler.JobFactory = service.GetService<IJobFactory>();
                return scheduler;
            });
            //構建容器
            return services.BuildServiceProvider();
        }

        public bool Start(HostControl hostControl)
        {
            var scheduler = _serviceProvider.GetService(typeof(IScheduler)) as IScheduler;
            scheduler.Start();
            return true;
        }

        public bool Stop(HostControl hostControl)
        {
            var scheduler = _serviceProvider.GetService(typeof(IScheduler)) as IScheduler;
            scheduler.Shutdown(true);
            return true;
        }
    }

  Test2Job.cs

    public class Test2Job : IJob
    {
        private ITest _testService;
        private AppSetting _appsetting;
        private IConfiguration _configuration;

        public Test2Job(ITest testService, IOptionsMonitor<AppSetting> appSettingAccessor, IConfiguration configuration)
        {
            _testService = testService;
            _appsetting = appSettingAccessor.CurrentValue;
            _configuration = configuration;
        }
        public Task Execute(IJobExecutionContext context)
        {
            Console.WriteLine($"job2222222222222 started:{_appsetting.TestCN}");
            var t = _testService.Dowork(1);
            t.Wait();
            Console.WriteLine($"job2222222222222 ended:{_configuration["TestCN"]}");

            return Task.CompletedTask;
        }
    }

  

  專案結構截圖

附專案原始碼 https://gitee.com/gt1987/gt.dotnet