随笔 - 32  文章 - 0 评论 - 18 阅读 - 28426
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

  在我们日常开发工作中,经常会运用到Quartz+Topshelf组件的组合来开发一些定时任务。那么在.Net Core下如何去使用呢?我自己尝试搭建了一个测试项目,过程中遇到了以下一些问题:

  • Quartz 配置文件及版本问题。我们知道Quartz有2个配置文件,quartz.config和quartz.job.xml。前者负责组件初始化配置,后者负责job和triggle的配置。刚开始我是直接把framework下的配置文件直接拿过来用的,启动直接报错。主要问题在quartz.config
    1
    quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin, Quartz

    这一段上。原因Quartz最新版本已经将Plugin模块单独剥离出一个独立的DLL,这里的引用也要变化。需要Nuget上下载Quartz.Plugins组件,并将上一段改成

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

     

  • DI问题。为了贴合.Net Core DI精神,我们也要来实现Console程序的DI功能。第一个问题是Job如何DI?首先我们需要自己去实现JobFactory

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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上下载的组件包(见后面项目结构截图)。加载方式如下
  • 1
    2
    3
    4
    5
    6
    7
    8
    private IConfiguration ConfigureConfiguration()
    {           
        //配置文件
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
        return builder.Build();
    }

      注入方式如下

    1
    2
    3
    4
    5
    6
    7
    8
    if (configuration != null)
    {
        //iconfiguration注入
        services.AddSingleton<IConfiguration>(configuration);
    }
     
    //自定义 option方式注入
    services.Configure<AppSetting>(configuration);

      这样就可以在后续的类代码中 直接通过DI方式获取IConfiguration对象或者你自己的Option对象了

  贴上完整代码。

  Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
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 启动的核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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.dotnetcore

posted on   gt1987  阅读(1470)  评论(2编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示