翼度科技»论坛 编程开发 .net 查看内容

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

2

主题

2

帖子

6

积分

新手上路

Rank: 1

积分
6

前言

在计算机系统中,定时执行一些后台任务是很常见的场景,比如定时发送邮件、备份数据等等。
那么,.NET 技术如何通过编程灵活地实现项目里复杂的自定义任务呢?
如果是 Windows 生态,通常来说,可以有这些方式:

  • 编写一个程序,通过 Windows 内置的任务计划来定时执行。
  • 编写一个程序,通过 Windows 内置的 Services 来定时执行。
  • 编写一个定时循环执行任务的程序,在 Windows 系统启动时配置为自动执行。
    ……
但是,如果是一个中小型的 Web 应用系统,这些方法方式就显得不太合适。Asp.net core Webapi 有没有办法执行定时任务呢?答案是有的,Asp.net core Webapi 可以通过常驻后台的托管服务来执行定时任务。
本文是 Asp.net core Webapi 运行一个常驻后台并从数据库中导出数据的托管服务的例子,写出来供大家指点,在讨论过程中共同提高水平。
Step By Step 实现步骤


  • 创建一个 asp.net core webapi 项目
  • 从 Nuget 安装以下包
    Microsoft.AspNetCore.Identity.EntityFrameworkCore
    Microsoft.EntityFrameworkCore.Relational
    Microsoft.EntityFrameworkCore.SqlServer
    Microsoft.EntityFrameworkCore.Tools

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

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

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

来源:https://www.cnblogs.com/JackyGz/Undeclared/17928776.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具