8.4 托管服务
有些工作是需要后台运行的,比如每天凌晨备份数据库。ASP.NET Core提供了托管服务来供我们编写后台代码。
托管服务只需要实现IHostedService
即可,一般在开发时编写继承自BackgroundService
的类,该类不进实现了IHostedService
接口,并且处理了任务取消等逻辑,我们只需实现BackgroundService
中定义的ExecuteAsync
方法即可。
托管服务案例:
publicclassDemoBgService : BackgroundService
{
privateILogger<DemoBgService>logger;
publicDemoBgService(ILogger<DemoBgService>logger)
{
this.logger=logger;
}
protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken)
{
awaitTask.Delay(5000);
strings=awaitFile.ReadAllTextAsync("d:/1.txt");
awaitTask.Delay(20000);
logger.LogInformation(s);
}
}
在Program.cs中进行注册
builder.Services.AddHostedService<DemoBgService>();
托管服务会随着应用程序的启动而启动,如果托管服务出现异常且没有捕获,则整个程序就会崩掉,设置HostOptions.BackgroundServiceExceptionBehavior
设置为Ignore,这样会忽略这个异常,但是不推荐这种设置,推荐使用Try,并把异常输出到日志。
托管服务中使用依赖注入的陷阱
长生命周期的服务不能依赖短依赖周期的服务,托管服务为单例声明周期,所以不能在托管服务中注入范围或者瞬态服务。
可以通过构造方法注入IServiceScopeFactory
服务,它可以用来创建IserviceScope对象
案例:
publicclassExplortStatisticBgService : BackgroundService
{
privatereadonlyTestDbContextctx;
privatereadonlyILogger<ExplortStatisticBgService>logger;
privatereadonlyIServiceScopeserviceScope;
//通过构造函数注入IServiceScopeFactory服务
publicExplortStatisticBgService(IServiceScopeFactoryscopeFactory)
{
this.serviceScope=scopeFactory.CreateScope();//通过Factory创建IServiceScope对象
varsp=serviceScope.ServiceProvider;
//获得Scope对象
this.ctx=sp.GetRequiredService<TestDbContext>();
this.logger=sp.GetRequiredService<ILogger<ExplortStatisticBgService>>();
}
protectedoverrideasyncTaskExecuteAsync(CancellationTokenstoppingToken)
{
while (!stoppingToken.IsCancellationRequested)//长期的后台服务,一直运行在后台
{
try//使用try
{
awaitDoExecuteAsync();
awaitTask.Delay(5000);
}
catch (Exceptionex)
{
logger.LogError(ex, "获取用户统计数据失败");
awaitTask.Delay(1000);
}
}
}
privateasyncTaskDoExecuteAsync()
{
varitems=ctx.Users.GroupBy(u=>u.CreationTime.Date)
.Select(e=>new { Date=e.Key, Count=e.Count() });
StringBuildersb=newStringBuilder();
sb.AppendLine($"Date:{DateTime.Now}");
foreach (variteminitems)
{
sb.Append(item.Date).AppendLine($":{item.Count}");
}
awaitFile.WriteAllTextAsync("d:/1.txt", sb.ToString());
logger.LogInformation($"导出完成");
}
publicoverridevoidDispose()
{
base.Dispose();
serviceScope.Dispose();//要释放
}
}