asp.net core 下,新增了一个BackgroundService用来实现能在后台跑一个长久运行的任务,因此,也可以用来替换掉原来使用的static的Timer组件,

Timer组件主要有以下几个麻烦的地方

1.如果是需要长时间跑的定时任务,需要定义为static,,在asp.net core下,无法利用到DI,无法从DI中获取DbContext之类的

2.启动定时器的时候,需要在start.cs自己手动启动

3.Timer是传入处理函数的方式,如果有好几个定时器,拼在一起,代码会看起来比较乱

4.使用Timer也无法实现corn表达式

5.Timer中也无法使用async异步处理

首先,我们来实现一个简单的定时器功能,用来替换掉Timer类:

1.TimerHostedService 定时器的基类

 1 /// <summary>
2 /// 用于在后台执行一个定时任务,用于取代TimeEx,在asp.net core的环境下使用,继承该类后,使用 services.AddHostedService<当前类类型>();后,自动在后台启动当前定时任务
3 /// </summary>
4 public abstract class TimerHostedService : BackgroundService
5 {
6 private IServiceProvider _provider = null;
7
8 protected TimerHostedService(IServiceProvider provider)
9 {
10 _provider = provider;
11 }
12
13 protected override async Task ExecuteAsync(CancellationToken stoppingToken)
14 {
15 if (Enabled && Internal>0) //如果启动服务的时候,Enabled为false或者不设置间隔时间,则该定时器永久不启动
16 {
17 while (!stoppingToken.IsCancellationRequested) //如果站点触发停止,则会使用stoppingToken的IsCancellactionRequest判断是否由IIS之类的停止应用
18 {
19 await Task.Delay(Internal, stoppingToken);
20
21 if (!stoppingToken.IsCancellationRequested)
22 {
23 using (var scope = _provider.CreateScope())
24 {
25 try
26 {
27 await Run(scope.ServiceProvider, stoppingToken);
28 }
29 catch (Exception e)
30 {
31
32 }
33 }
34 }
35
36
37 }
38 }
39
40
41
42 return;
43 }
44
45 /// <summary>
46 /// 实际执行的定时器处理函数
47 /// </summary>
48 /// <param name="serviceScope">当次的Ioc容器,可获取当前程序中用于注入的容器内的类</param>
49 /// <param name="stoppingToken">是否暂停</param>
50 /// <returns></returns>
51 protected abstract Task Run(IServiceProvider serviceProvider, CancellationToken stoppingToken);
52
53 /// <summary>
54 /// 定时器间隔触发时间,单位是ms
55 /// </summary>
56 protected abstract int Internal { get; }
57
58 /// <summary>
59 /// 当前定时器是否启用,true为定时器有效,false为停用
60 /// </summary>
61 public virtual bool Enabled { set; get; } = true;
62 }

2.实现一个定时器:

 1 /// <summary>
2 /// 检查订单是否完成的后台任务
3 /// </summary>
4 public class CheckOrderCompleteTask:TimerHostedService
5 {
6 public CheckOrderCompleteTask(IServiceProvider provider) : base(provider)
7 {
8 this.Enabled = CustomConfigManager.Default["Timer:CheckBlessCompleted"].ToBool();
9 }
10
11 protected override async Task Run(IServiceProvider serviceProvider, CancellationToken stoppingToken)
12 {
13 var s = (XXXService) serviceProvider.GetService(typeof(XXXService)); //可以从DI容器中获取service或者dbcontext
17 await s.CheckXXX(stoppingToken); //此处为实际执行的定时任务处理函数
18 }
19
20 protected override int Internal { get; } = 1000 * 60 * 5;
21 }

在Start.cs中,注册该任务:

1 public void ConfigureServices(IServiceCollection services)
2 {
3 //.....其他代码
4 services.AddHostedService<CheckOrderCompleteTask>(); //此处将服务注册后,即由asp.net core在启动后,自动启动该服务
5 }

这样,就会有asp.net core自动启动该任务

由于上面定义的是一个定时器,有时候需要比如半夜12点,或者中午12点运行,这种场景下,就需要使用到计划任务的组件了,,.net下,常用的,一般有hangfire跟Quartz.Net,,这两个组件功能比较完善,而且也带有管理功能,but,..就是有时候复杂了点,通常有些不复杂的计划任务,比如又不想直接引入那么复杂的组件,那么可以根据上面的定时组件,变化出一个简单的计划任务组件:

    /// <summary>
/// 一个简单的corn模式的计划任务<br/>用于在一些已知的计划时间执行某些任务的情况下使用,Cron属性在服务启动后,变无法修改,如需配置运行时可修改,请使用Hangfire之类的其他第三方框架
/// </summary>
public abstract class SimpleScheduledTaskService : BackgroundService
{
private IServiceProvider _provider = null;
private CrontabSchedule _crontab = null;
private string _cron;
private bool _enabled=true;
private bool _isInited = false; protected SimpleScheduledTaskService(IServiceProvider provider)
{
_provider = provider;
} protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
_crontab = CrontabSchedule.Parse(_cron); //解析cron字符串
}
catch (Exception e)
{
throw;
} while (Enabled && _crontab != null && !stoppingToken.IsCancellationRequested)
{ var nextDt = _crontab.GetNextOccurrence(DateTime.Now.AddSeconds(2)); var interval = (nextDt - DateTime.Now); await Task.Delay(interval, stoppingToken); var logger = (ILogger)_provider.GetService(typeof(ILogger)); try
{
logger?.Log(LogLevel.Trace, $"启动计划任务:{this.GetType().Name}"); await Run(_provider, stoppingToken); logger?.Log(LogLevel.Trace, $"完成计划任务:{this.GetType().Name}");
}
catch (Exception e)
{
logger?.Log(LogLevel.Error, e, $"计划任务执行异常:{e.Message}");
}
}
} protected abstract Task Run(IServiceProvider provider, CancellationToken stoppingToken); /// <summary>
/// 计划任务的Cron配置字符串,可使用在线生成器生成后,填入
/// </summary>
public virtual string Cron
{
get => _cron;
} /// <summary>
/// 计划任务是否启动
/// </summary>
public virtual bool Enabled
{
set => _enabled = value;
get => _enabled;
}
}

上述使用类似Timer的方式,,通过计算cron表达式计算后的结果与当前时间差,delay指定时间后触发,这个功能,一般只能用在一些不是特别重要的定时任务,并且不需要补偿的环境下

通常我都是用比如:https://cron.qqe2.com/之类的在线生成cron表达式的网站生成

计划任务使用的第三方组件为:

NCrontab : https://github.com/atifaziz/NCrontab

上述源码地址:

TimerHostedService: https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core.NetCore/Services/TimerHostedService.cs

SimpleScheduledTaskService : https://github.com/kugarliyifan/Kugar.Core/blob/master/Kugar.Core.NetCore/Services/ScheduledTaskService.cs

C#编写一个在asp.net core 3.1下的简单的corn模式的计划任务和一个更简单的定时器类的更多相关文章

  1. ASP.NET Core Web API下事件驱动型架构的实现(一):一个简单的实现

    很长一段时间以来,我都在思考如何在ASP.NET Core的框架下,实现一套完整的事件驱动型架构.这个问题看上去有点大,其实主要目标是为了实现一个基于ASP.NET Core的微服务,它能够非常简单地 ...

  2. 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(1)

    最近使用vscode比较多. 学习了一下如何在mac上使用vscode开发asp.netcore项目. 这里是我写的关于vscode的一篇文章: https://www.cnblogs.com/cgz ...

  3. ASP.NET Core模块化前后端分离快速开发框架介绍之2、快速创建一个业务模块

    源码地址 GitHub:https://github.com/iamoldli/NetModular 演示地址 地址:https://nm.iamoldli.com 账户:admin 密码:admin ...

  4. ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...

  5. 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线

    重温.NET下Assembly的加载过程   最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...

  6. [ASP.NET Core 3框架揭秘] 依赖注入:IoC模式

    原文:[ASP.NET Core 3框架揭秘] 依赖注入:IoC模式 正如我们在<依赖注入:控制反转>提到过的,很多人将IoC理解为一种“面向对象的设计模式”,实际上IoC不仅与面向对象没 ...

  7. 用VSCode开发一个基于asp.net core 2.0/sql server linux(docker)/ng5/bs4的项目(2)

    第一部分: http://www.cnblogs.com/cgzl/p/8478993.html 为Domain Model添加约束 前一部分, 我们已经把数据库创建出来了. 那么我们先看看这个数据库 ...

  8. 如何从40亿整数中找到不存在的一个 webservice Asp.Net Core 轻松学-10分钟使用EFCore连接MSSQL数据库 WPF实战案例-打印 RabbitMQ与.net core(五) topic类型 与 headers类型 的Exchange

    如何从40亿整数中找到不存在的一个 前言 给定一个最多包含40亿个随机排列的32位的顺序整数的顺序文件,找出一个不在文件中的32位整数.(在文件中至少确实一个这样的数-为什么?).在具有足够内存的情况 ...

  9. 从零开始构建一个的asp.net Core 项目(二)

    接着上一篇博客继续进行.上一篇博客只是显示了简单的MVC视图页,这篇博客接着进行,连接上数据库,进行简单的CRUD. 首先我在Controllers文件夹点击右键,添加->控制器 弹出的对话框中 ...

随机推荐

  1. docker 使用笔记

    docker 使用笔记 1. 与宿主机之间拷贝文件 docker cp test.html 99f952ac05e6cd879f14aa6c9d0db02aaf498634edc4f6cdc9953c ...

  2. Android驱动学习-内部机制_回顾binder框架关键点

    内部机制_回顾binder框架关键点server注册服务时, 对每个服务都提供不同的ptr/cookie,在驱动程序里对每个服务都构造一个binder_node, 它也含有ptr/cookie cli ...

  3. 第四章节 BJROBOT 线速度校正 【ROS全开源阿克曼转向智能网联无人驾驶车】

    BJROBOT 线速度校正   1.把小车平放在地板上,用卷尺作为测量刻度,选取车头或者车尾处作为小车的起点, 打开资料里的虚拟机,打开一个终端 ssh 过去主控端启动 roslaunch znjro ...

  4. MySQL不会丢失数据的秘密,就藏在它的 7种日志里

    本文收录在 GitHub 地址 https://github.com/chengxy-nds/Springboot-Notebook 进入正题前先简单看看MySQL的逻辑架构,相信我用的着. MySQ ...

  5. 关于Webservice接口对接相关总结

    Webservice接口对接 因为近期处理了很多关于Webservice的接口对接,所以这篇文章是对近期自己的学习做一个简单的总结. 一: 对于接口对接,建议首先需要了解一下WSDL文件,以及入参的S ...

  6. JVM -- 类加载的过程

    类的加载过程? 一个Java文件从编码完成到最终执行,一般主要包括"编译"和"运行"两个过程.编译,即把我们写好的java文件,通过javac命令编译成字节码, ...

  7. 2021.Jan.11.Mon

            人生中的机会不会有很多次,把每一次机会都当作最后一次机会,全力以赴地去做好,那么,你就不会永远在等待下一个机会. 看书心得---------By VincentAdamNemessis ...

  8. 【递归】P5461赦免战俘

    题目相关 原题链接:P5461 赦免战俘 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目背景 借助反作弊系统,一些在月赛有抄袭作弊行为的选手被抓出来了! 题目描述 现有 \(2 ...

  9. WPF + RDLC + 动态生成列 + 表头合并

    如下,评论超过20条,马上发代码*(੭*ˊᵕˋ)੭*ଘ,效果如下: 代码逻辑简单. WPF使用RDLC需要使用如下DLL 新建WPF 窗体,黏贴下大概如下 <Window xmlns:rv=&q ...

  10. docker进入mysql数据库并进行导入 导出

    一:导入 1.首先查看docker运行的容器: docker ps 2.将宿主机文件拷贝到docker容器中: docker cp 2020415.sql af491d5466ea:/opt/2020 ...