ASP.NET Core托管服务
目录
托管服务的异常问题
托管服务中使用DI
托管服务案例:数据的定时导出
场景,代码运行在后台。比如服务器启动的时候在后台预先加载数据到缓存,每天凌晨3点把数据导出到备份数据库,每隔5秒钟在两张表之间同步一次数据。托管服务实现IHostedService接口,一般编写从BackgroundService继承的类。如需定时操作,可使用Hangfire框架。
builder.Services.AddHostedService<HostedService>();
托管服务的异常问题
- 从.NET 6开始,当托管服务中发生未处理异常的时候,程序就会自动停止并退出。可以把HostOptions.BackgroundServiceExceptionBehavior设置为Ignore,程序会忽略异常,而不是停止程序。不过推荐采用默认的设置,因为“异常应该被妥善的处理,而不是被忽略”。
- 要在ExecuteAsync方法中把代码用try……catch包裹起来,当发生异常的时候,记录日志中或发警报等。
托管服务中使用DI
- 托管服务是以单例的生命周期注册到依赖注入容器中的。因此不能注入生命周期为范围或者瞬态的服务。比如注入EF Core的上下文的话,程序就会抛出异常。
- 可以通过构造方法注入一个IServiceScopeFactory服务,它可以用来创建一个IServiceScope对象,这样我们就可以通过IServiceScope来创建短生命周期的服务了。记得在Dispose中释放IServiceScope。
public class HostedService : BackgroundService
{
private IServiceScope serviceScope;
public HostedService(IServiceScopeFactory serviceScopeFactory)
{
this.serviceScope = serviceScopeFactory.CreateScope();
}
public override void Dispose()
{
this.serviceScope.Dispose();
base.Dispose();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var testService = serviceScope.ServiceProvider.GetRequiredService<TestService>();
Console.WriteLine("HostService1启动" + testService.Add(1, 1));
await Task.Delay(3000);
string txt = await File.ReadAllTextAsync("f:/1.txt");
Console.WriteLine("文件读取完成");
string s = null;
s.ToString();
await Task.Delay(3000);
Console.WriteLine(txt);
}
catch (Exception ex)
{
Console.WriteLine("服务中出现未处理异常" + ex);
}
}
}
public class TestService
{
public int Add(int a, int b)
{
return a + b;
}
}
builder.Services.AddHostedService<HostedService>();
builder.Services.AddScoped<TestService>();
托管服务案例:数据的定时导出
常驻后台的托管服务并不需要特殊的技术,我们只要让ExecuteAsync中的代码一直执行不结束就行了。实现的功能就是每隔五秒钟统计一次数据库中的数据,将数据写入文本。
builder.Services.AddHostedService<ScheduledService>();
public class ScheduledService : BackgroundService
{
private readonly IServiceScope serviceScope;
public ScheduledService(IServiceScopeFactory serviceScopeFactory)
{
this.serviceScope = serviceScopeFactory.CreateScope();
}
public override void Dispose()
{
this.serviceScope.Dispose();
base.Dispose();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var dbCtx = serviceScope.ServiceProvider.GetRequiredService<MyDbContext>();
while (!stoppingToken.IsCancellationRequested)
{
var count = dbCtx.Users.LongCount();
await File.WriteAllTextAsync("F:/1.txt", count.ToString());
await Task.Delay(5000);
}
Console.WriteLine("导出成功" + DateTime.Now);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}