前言

在计算机系统中,定时执行一些后台任务是很常见的场景,比如定时发送邮件、备份数据等等。

那么,.NET 技术如何通过编程灵活地实现项目里复杂的自定义任务呢?

如果是 Windows 生态,通常来说,可以有这些方式:

  1. 编写一个程序,通过 Windows 内置的任务计划来定时执行。
  2. 编写一个程序,通过 Windows 内置的 Services 来定时执行。
  3. 编写一个定时循环执行任务的程序,在 Windows 系统启动时配置为自动执行。

    ……

但是,如果是一个中小型的 Web 应用系统,这些方法方式就显得不太合适。Asp.net core Webapi 有没有办法执行定时任务呢?答案是有的,Asp.net core Webapi 可以通过常驻后台的托管服务来执行定时任务。

本文是 Asp.net core Webapi 运行一个常驻后台并从数据库中导出数据的托管服务的例子,写出来供大家指点,在讨论过程中共同提高水平。

Step By Step 实现步骤

  1. 创建一个 asp.net core webapi 项目
  2. 从 Nuget 安装以下包

    Microsoft.AspNetCore.Identity.EntityFrameworkCore

    Microsoft.EntityFrameworkCore.Relational

    Microsoft.EntityFrameworkCore.SqlServer

    Microsoft.EntityFrameworkCore.Tools

  3. 打开 appsettings.json 并添加数据库连接字符串,如:
    {
    "Logging": {
    "LogLevel": {
    "Default": "Information",
    "Microsoft.AspNetCore": "Warning"
    }
    },
    "AllowedHosts": "*",
    "ConnectionStrings": {
    "Default": "Server=(localdb)\\mssqllocaldb;Database=IdentityTestDB;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
    }
  4. 添加一个继承于 IdentityUser 的 User 类
    using Microsoft.AspNetCore.Identity;
    
    public class User: IdentityUser<long>
    {
    public DateTime CreationTime { get; set; }
    public string? NickName { get; set; }
    }
  5. 添加一个继承于 IdentityRole 的 Role 类
    using Microsoft.AspNetCore.Identity;
    
    public class Role: IdentityRole<long>
    { }
  6. 创建数据库上下文
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore; public class TestDbContext: IdentityDbContext<User, Role, long>
    {
    public TestDbContext(DbContextOptions<TestDbContext> options):base(options)
    { } protected override void OnModelCreating(ModelBuilder builder)
    {
    base.OnModelCreating(builder);
    builder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
    }
    }
  7. 创建一个 ExplortStatisticBgService 类并继承 BackgroundService,这是托管服务类
    using Microsoft.EntityFrameworkCore;
    using System.Text; public class ExplortStatisticBgService : BackgroundService
    {
    private readonly TestDbContext ctx;
    private readonly ILogger<ExplortStatisticBgService> logger;
    private readonly IServiceScope serviceScope; /// <summary>
    /// 在构造方法注入IServiceScopeFactory服务,
    /// 用来创建IServiceScope对象,
    /// 这样就可以通过IServiceScope来创建短生命周期的服务了
    /// </summary>
    /// <param name="scopeFactory"></param>
    public ExplortStatisticBgService(IServiceScopeFactory scopeFactory)
    {
    this.serviceScope = scopeFactory.CreateScope();
    var sp = serviceScope.ServiceProvider;
    this.ctx = sp.GetRequiredService<TestDbContext>();
    this.logger = sp.GetRequiredService<ILogger<ExplortStatisticBgService>>();
    } protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
    // 用 while 循环实现服务常驻
    while (!stoppingToken.IsCancellationRequested)
    {
    // 用 try...catch 捕捉异常记录错误信息并避免方法退出
    try
    {
    // 这里实现每隔5秒从数据库中导出数据
    // 更复杂的配置可以用第三方开源的框架
    await DoExecuteAsync();
    await Task.Delay(5000);
    }
    catch (Exception ex)
    {
    logger.LogError(ex, "获取用户统计数据失败");
    await Task.Delay(1000);
    }
    }
    } private async Task DoExecuteAsync()
    {
    var items = ctx.Users.AsNoTracking().GroupBy(u => u.CreationTime.Date)
    .Select(e => new
    {
    Date = e.Key,
    Count = e.Count()
    });
    StringBuilder sb = new StringBuilder(1024);
    sb.AppendLine($"Date: {DateTime.Now}");
    foreach (var item in items)
    {
    sb.Append(item.Date).AppendLine($": {item.Count}");
    }
    await File.WriteAllTextAsync("d:/1.txt", sb.ToString());
    logger.LogInformation($"导出完成");
    } /// <summary>
    /// IServiceScope 需要释放
    /// 所以重写 Dispose 方法
    /// </summary>
    public override void Dispose()
    {
    base.Dispose();
    serviceScope.Dispose();
    }
    }
  8. 打开 Program.cs,注入托管服务等,看代码的注释
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers();
    // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen(); IServiceCollection services = builder.Services; // 注册托管服务
    services.AddHostedService<ExplortStatisticBgService>(); // 注入数据库上下文
    services.AddDbContext<TestDbContext>(options => {
    string connStr = builder.Configuration.GetConnectionString("Default")!;
    options.UseSqlServer(connStr);
    }); // 数据保护服务注入
    // ----数据保护提供了一个简单、基于非对称加密改进的加密API用于确保Web应用敏感数据的安全存储
    // ----不需要开发人员自行生成密钥,它会根据当前应用的运行环境,生成该应用独有的一个私钥
    services.AddDataProtection(); // 注入 Identity 框架的一些重要的基础配置
    // 如果没有这个,下面的注入 UserManager 等服务会有问题,程序无法编译
    services.AddIdentityCore<User>(options =>
    {
    options.Password.RequireDigit = false;
    options.Password.RequireLowercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
    options.Password.RequiredLength = 6;
    options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
    options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
    }); // 注入 UserManager、RoleManager 等Identity 框架服务
    var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), services);
    idBuilder.AddEntityFrameworkStores<TestDbContext>()
    .AddDefaultTokenProviders()
    .AddRoleManager<RoleManager<Role>>()
    .AddUserManager<UserManager<User>>(); var app = builder.Build(); // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
    app.UseSwagger();
    app.UseSwaggerUI();
    } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
    ``
  9. Ctrl+F5 运行项目,不做任何操作,托管程序会自动导出数据

扩展

托管服务在后台运行,通过它可以实现在很多事情,比如:

  1. 监控消息队列,当有数据进入消息队列就处理。
  2. 再如每隔10s把A数据库中的数据同步到B数据库中
  3. ...... 等等

Asp.net core Webapi 如何执行定时任务?的更多相关文章

  1. ASP.Net Core WebApi几种版本控制对比

    版本控制的好处: (1)助于及时推出功能, 而不会破坏现有系统. (2)它还可以帮助为选定的客户提供额外的功能. API 版本控制可以采用不同的方式进行控制,方法如下: (1)在 URL 中追加版本或 ...

  2. ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了

    引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必是件很痛苦的事情吧,但文档又必须写,而且文档的格式如果没有具体要求的话,最终完成的文档则完全取决于开发者 ...

  3. ASP.NET Core WebApi使用Swagger生成api

    引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必是件很痛苦的事情吧,但文档又必须写,而且文档的格式如果没有具体要求的话,最终完成的文档则完全取决于开发者 ...

  4. ASP.NET Core WebApi使用Swagger生成api说明文档

    1. Swagger是什么? Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件 ...

  5. asp.net core webapi 使用ef 对mysql进行增删改查,并生成Docker镜像构建容器运行

    1.构建运行mysql容器,添加数据库user 参考Docker创建运行多个mysql容器,地址 http://www.cnblogs.com/heyangyi/p/9288402.html 添加us ...

  6. Docker 为 ASP.NET Core WebApi 应用程序生成 Docker 映像,创建容器并运行

    1.使用VS2017新建ASP.NET Core WebApi项目 选择API启用Docker支持 2.为 ASP.NET Core WebApi 应用程序生成 Docker 映像,并创建容器运行 生 ...

  7. 【转】ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了

    原文链接:https://www.cnblogs.com/yilezhu/p/9241261.html 引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必 ...

  8. ASP.NET CORE WEBAPI文件下载

    ASP.NET CORE WEBAPI文件下载 最近要使用ASP.NET CORE WEBAPI用来下载文件,使用的.NET CORE 3.1.考虑如下场景: 文件是程序生成的. 文件应该能兼容各种格 ...

  9. ionic + asp.net core webapi + keycloak实现前后端用户认证和自动生成客户端代码

    概述 本文使用ionic/angular开发网页前台,asp.net core webapi开发restful service,使用keycloak保护前台页面和后台服务,并且利用open api自动 ...

  10. 记一次使用Asp.Net Core WebApi 5.0+Dapper+Mysql+Redis+Docker的开发过程

    #前言 我可能有三年没怎么碰C#了,目前的工作是在全职搞前端,最近有时间抽空看了一下Asp.net Core,Core版本号都到了5.0了,也越来越好用了,下面将记录一下这几天以来使用Asp.Net ...

随机推荐

  1. Java程序员学vue3最好的方式就是搭建后台管理模板

    前言 作为Java程序员,vue3还是有必要学的,毕竟是国内最受欢迎的前端JS框架,你现在接手的项目,前端部分几乎都会和vue沾边,尤其是中小企业. vue3作为新的大版本,相较于vue2改动还是很多 ...

  2. 【SpringBoot实战】开发入门--快速创建springboot程序

    前言 本片博客记录快速创建springboot工程的使用spring initializr创建.开发环境JDK1.8.IDEA.maven. SpringBoot 优点 可快速构建spring应用 直 ...

  3. SpringBoot + Redis + Token 解决接口幂等性问题

    前言 SpringBoot实现接口幂等性的方案有很多,其中最常用的一种就是 token + redis 方式来实现. 下面我就通过一个案例代码,帮大家理解这种实现逻辑. 原理 前端获取服务端getTo ...

  4. 2023-09-16:用go语言,给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p , 它们表示一个长度为 n 且下标从 0 开始的数组 arr , 数组中除了下标为 p 处是 1

    2023-09-16:用go语言,给你一个整数 n 和一个在范围 [0, n - 1] 以内的整数 p , 它们表示一个长度为 n 且下标从 0 开始的数组 arr , 数组中除了下标为 p 处是 1 ...

  5. JavaAgent寄生在目标进程中引起的ClassNotFoundException

    今天有解决方案部的小伙伴反映,我公司XWind产品在分析客户应用程序的潜在性能问题时,总是显现诊断任务异常,为了定位问题的根因,我们马上要求解决方案部的小伙伴提供XWind相关的日志,从日志中找到了如 ...

  6. 小米手机抓取Log教程

    当小米手机遇到任何系统下的较为严重的故障时(即此时系统还是基本正常运行的,只是某些功能实现出了问题),例如软件自动退出,SD卡自动卸载,电话无法拨出等等.可以请用户协助抓取log即系统运行日志,来发送 ...

  7. 几个易错的python小知识点

    大家好,我是暴走の海鸽~ 本期整理了几个基础python防坑小常识,希望对大家有所帮助. 1. type == object? 执行以下代码的结果是什么: >>> isinstanc ...

  8. Substring of Sorted String 题解

    Substring of Sorted String 写篇题解纪念一下蒟蒻第一次赛时切出的 F 题. 题目简述 对一个字符串进行单点修改,区间判断操作. 修改操作为将一个字符修改为另一个,判断操作为判 ...

  9. 关于Halcon中variation_model模型的快速解读。

    十一期间在家用期间研读了下Halcon的variation_model模型,基本上全系复现了他的所有技术要求和细节,这里做个记录. 其实这个模型的所有原理都不是很复杂的,而且Halcon中的帮助文档也 ...

  10. 如何去掉桌面快捷方式左下角的小箭头(Win11)

    在对系统重命名之后,在快捷方式的左下角莫名的出现了小图标 如果想要去掉这个小图标 (1)首先在桌面上创建一个txt文件 (2)打开后输入指令 reg add "HKEY_LOCAL_MACH ...