8.4 托管服务
有些工作是需要后台运行的,比如每天凌晨备份数据库。ASP.NET Core提供了托管服务来供我们编写后台代码。
托管服务只需要实现IHostedService
即可,一般在开发时编写继承自BackgroundService
的类,该类不进实现了IHostedService
接口,并且处理了任务取消等逻辑,我们只需实现BackgroundService
中定义的ExecuteAsync
方法即可。
托管服务案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class DemoBgService : BackgroundService { private ILogger<DemoBgService> logger; public DemoBgService(ILogger<DemoBgService> logger) { this.logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { await Task.Delay(5000); string s = await File.ReadAllTextAsync("d:/1.txt"); await Task.Delay(20000); logger.LogInformation(s); } }
|
在Program.cs中进行注册
1
| builder.Services.AddHostedService<DemoBgService>();
|
托管服务会随着应用程序的启动而启动,如果托管服务出现异常且没有捕获,则整个程序就会崩掉,设置HostOptions.BackgroundServiceExceptionBehavior
设置为Ignore,这样会忽略这个异常,但是不推荐这种设置,推荐使用Try,并把异常输出到日志。
托管服务中使用依赖注入的陷阱
长生命周期的服务不能依赖短依赖周期的服务,托管服务为单例声明周期,所以不能在托管服务中注入范围或者瞬态服务。
可以通过构造方法注入IServiceScopeFactory
服务,它可以用来创建IserviceScope对象
案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class ExplortStatisticBgService : BackgroundService { private readonly TestDbContext ctx; private readonly ILogger<ExplortStatisticBgService> logger; private readonly IServiceScope serviceScope; 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 (!stoppingToken.IsCancellationRequested) { try { await DoExecuteAsync(); await Task.Delay(5000); } catch (Exception ex) { logger.LogError(ex, "获取用户统计数据失败"); await Task.Delay(1000); } } } private async Task DoExecuteAsync() { var items = ctx.Users.GroupBy(u => u.CreationTime.Date) .Select(e => new { Date = e.Key, Count = e.Count() }); StringBuilder sb = new StringBuilder(); 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($"导出完成"); } public override void Dispose() { base.Dispose(); serviceScope.Dispose(); } }
|