Asp.net core Webapi 如何执行定时任务?

简介: Asp.net core Webapi 如何执行定时任务?

前言

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

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

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

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

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

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

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

Microsoft.AspNetCore.Identity.EntityFrameworkCore

Microsoft.EntityFrameworkCore.Relational

Microsoft.EntityFrameworkCore.SqlServer

Microsoft.EntityFrameworkCore.Tools

Step By Step 实现步骤

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. … 等等


相关文章
|
5天前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
23 5
|
18天前
|
开发框架 .NET 程序员
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
Autofac 是一个轻量级的依赖注入框架,专门为 .NET 应用程序量身定做,它就像是你代码中的 "魔法师",用它来管理对象的生命周期,让你的代码更加模块化、易于测试和维护
驾驭Autofac,ASP.NET WebApi实现依赖注入详细步骤总结
|
24天前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
35 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
13天前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
22 3
|
2月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
开发框架 .NET 中间件
ASP.NET Core 面试题(二)
ASP.NET Core 面试题(二)
330 0
|
开发框架 JSON .NET
ASP.NET Core 面试题(一)
ASP.NET Core 面试题(一)
983 0
|
3月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
46 7
|
3月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
74 0
|
4月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
55 0