前言
在計(jì)算機(jī)系統(tǒng)中,定時(shí)執(zhí)行一些后臺(tái)任務(wù)是很常見的場(chǎng)景,比如定時(shí)發(fā)送郵件、備份數(shù)據(jù)等等。
那么,.NET 技術(shù)如何通過編程靈活地實(shí)現(xiàn)項(xiàng)目里復(fù)雜的自定義任務(wù)呢?
如果是 Windows 生態(tài),通常來說,可以有這些方式:
- 編寫一個(gè)程序,通過 Windows 內(nèi)置的任務(wù)計(jì)劃來定時(shí)執(zhí)行。
- 編寫一個(gè)程序,通過 Windows 內(nèi)置的 Services 來定時(shí)執(zhí)行。
- 編寫一個(gè)定時(shí)循環(huán)執(zhí)行任務(wù)的程序,在 Windows 系統(tǒng)啟動(dòng)時(shí)配置為自動(dòng)執(zhí)行。
……
但是,如果是一個(gè)中小型的 Web 應(yīng)用系統(tǒng),這些方法方式就顯得不太合適。Asp.net core Webapi 有沒有辦法執(zhí)行定時(shí)任務(wù)呢?答案是有的,Asp.net core Webapi 可以通過常駐后臺(tái)的托管服務(wù)來執(zhí)行定時(shí)任務(wù)。
本文是 Asp.net core Webapi 運(yùn)行一個(gè)常駐后臺(tái)并從數(shù)據(jù)庫中導(dǎo)出數(shù)據(jù)的托管服務(wù)的例子,寫出來供大家指點(diǎn),在討論過程中共同提高水平。
Step By Step 實(shí)現(xiàn)步驟
- 創(chuàng)建一個(gè) asp.net core webapi 項(xiàng)目
- 從 Nuget 安裝以下包
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Relational
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools文章來源:http://www.zghlxwxcb.cn/news/detail-760677.html - 打開 appsettings.json 并添加數(shù)據(jù)庫連接字符串,如:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "Default": "Server=(localdb)\\mssqllocaldb;Database=IdentityTestDB;Trusted_Connection=True;MultipleActiveResultSets=true" } }
- 添加一個(gè)繼承于 IdentityUser 的 User 類
using Microsoft.AspNetCore.Identity; public class User: IdentityUser<long> { public DateTime CreationTime { get; set; } public string? NickName { get; set; } }
- 添加一個(gè)繼承于 IdentityRole 的 Role 類
using Microsoft.AspNetCore.Identity; public class Role: IdentityRole<long> { }
- 創(chuàng)建數(shù)據(jù)庫上下文
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); } }
- 創(chuàng)建一個(gè) ExplortStatisticBgService 類并繼承 BackgroundService,這是托管服務(wù)類
using Microsoft.EntityFrameworkCore; using System.Text; public class ExplortStatisticBgService : BackgroundService { private readonly TestDbContext ctx; private readonly ILogger<ExplortStatisticBgService> logger; private readonly IServiceScope serviceScope; /// <summary> /// 在構(gòu)造方法注入IServiceScopeFactory服務(wù), /// 用來創(chuàng)建IServiceScope對(duì)象, /// 這樣就可以通過IServiceScope來創(chuàng)建短生命周期的服務(wù)了 /// </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 循環(huán)實(shí)現(xiàn)服務(wù)常駐 while (!stoppingToken.IsCancellationRequested) { // 用 try...catch 捕捉異常記錄錯(cuò)誤信息并避免方法退出 try { // 這里實(shí)現(xiàn)每隔5秒從數(shù)據(jù)庫中導(dǎo)出數(shù)據(jù) // 更復(fù)雜的配置可以用第三方開源的框架 await DoExecuteAsync(); await Task.Delay(5000); } catch (Exception ex) { logger.LogError(ex, "獲取用戶統(tǒng)計(jì)數(shù)據(jù)失敗"); 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($"導(dǎo)出完成"); } /// <summary> /// IServiceScope 需要釋放 /// 所以重寫 Dispose 方法 /// </summary> public override void Dispose() { base.Dispose(); serviceScope.Dispose(); } }
- 打開 Program.cs,注入托管服務(wù)等,看代碼的注釋
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; // 注冊(cè)托管服務(wù) services.AddHostedService<ExplortStatisticBgService>(); // 注入數(shù)據(jù)庫上下文 services.AddDbContext<TestDbContext>(options => { string connStr = builder.Configuration.GetConnectionString("Default")!; options.UseSqlServer(connStr); }); // 數(shù)據(jù)保護(hù)服務(wù)注入 // ----數(shù)據(jù)保護(hù)提供了一個(gè)簡單、基于非對(duì)稱加密改進(jìn)的加密API用于確保Web應(yīng)用敏感數(shù)據(jù)的安全存儲(chǔ) // ----不需要開發(fā)人員自行生成密鑰,它會(huì)根據(jù)當(dāng)前應(yīng)用的運(yùn)行環(huán)境,生成該應(yīng)用獨(dú)有的一個(gè)私鑰 services.AddDataProtection(); // 注入 Identity 框架的一些重要的基礎(chǔ)配置 // 如果沒有這個(gè),下面的注入 UserManager 等服務(wù)會(huì)有問題,程序無法編譯 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 框架服務(wù) 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(); ``
- Ctrl+F5 運(yùn)行項(xiàng)目,不做任何操作,托管程序會(huì)自動(dòng)導(dǎo)出數(shù)據(jù)
擴(kuò)展
托管服務(wù)在后臺(tái)運(yùn)行,通過它可以實(shí)現(xiàn)在很多事情,比如:文章來源地址http://www.zghlxwxcb.cn/news/detail-760677.html
- 監(jiān)控消息隊(duì)列,當(dāng)有數(shù)據(jù)進(jìn)入消息隊(duì)列就處理。
- 再如每隔10s把A數(shù)據(jù)庫中的數(shù)據(jù)同步到B數(shù)據(jù)庫中
- ...... 等等
到了這里,關(guān)于Asp.net core Webapi 如何執(zhí)行定時(shí)任務(wù)?的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!