IHostService 托管服务(后台服务)
托管服务简介
1、场景<代码运行在后台。比如服务器启动的时候在 后台预先加载数据到缓存,每天凌晨3点把数据导出到 备份数据库,每隔5秒钟在两张表之间同步一次数据。
2、托管服务实现IHostedService
接,一般编写从
BackgroundService
继承的类。
测试:延迟若干秒再读取文件,再延迟,再输出。
3、 services.AddHostedService<DemoBgService>();
HostedDemo1Service.cs
namespace HostedService.BackgroundServices
{
public class HostedDemo1Service : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine(1111);
await Task.Delay(3000);
Console.WriteLine(22222);
}
}
}
Program.cs
builder.Services.AddHostedService<HostedDemo1Service>();
托管服务的异常问题
1、.NET6开始,当托管服务中发生未处理异常的时候,程序就会自动停止并退出。可以把HostOptions.BackgroundSeryiceExceptionBehavior
设置为Ignore
,程序会忽略异常而不是停止程序。不过推荐采用默认的设置,因为“异常应该被妥善的处理,而不是被忽略”。
2、要在ExecuteAsync
方法中把代码用try....catch
包裹起来,当发生异常的时候,记录日志中或发警报等。
namespace HostedService.BackgroundServices
{
public class HostedDemo1Service : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
Console.WriteLine(1111);
await Task.Delay(3000);
string txt = await File.ReadAllTextAsync("d:/1.txt");
Console.WriteLine(22222);
}
catch (Exception ex)
{
Console.WriteLine("程序中异常:" + ex.Message);
}
}
}
}
1111
程序中异常:Could not find file 'd:\1.txt'.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5184
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
托管服务中使用DI
1、托管服务是以单例
的生命周期注册到依赖注入容器中的。因此不能注入生命周期为范围或者瞬态的服务。比如注入EF Core的上下文的话,程序就会抛出异常。
2、可以通过构造方法注入一个IServiceScopeFactory
服务,它可以用来创建一个IServiceScope
对象,这样我们就可以通过IServiceScope
来创建短生命周期的服务了。
记得在Dispose中释放IServiceScope
。
namespace HostedService.BackgroundServices
{
public class HostedDemo1Service : BackgroundService
{
private readonly IServiceScope serviceScope;
public HostedDemo1Service(IServiceScopeFactory serviceScopefactory)
{
this.serviceScope = serviceScopefactory.CreateScope();
}
public override void Dispose()
{
this.serviceScope.Dispose();
base.Dispose();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
var demo1 = serviceScope.ServiceProvider.GetRequiredService<Demo1>();
int result = await demo1.Add(1, 2);
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine("程序中异常:" + ex.Message);
}
}
}
}
解释 GetService 与 GetRequiredService 的区别
重点说明它们在获取服务时的行为差异,特别是在服务不存在时的处理方式。
GetService
方法:
定义在 Microsoft.Extensions.DependencyInjection.IServiceProvider 接口中。
用于从服务容器中获取指定类型的服务。
如果容器中注册了该类型的服务,则返回对应的服务实例;如果没有注册,则返回 null。
调用者需要自行检查返回值是否为 null,以避免空引用异常。
GetRequiredService
方法:
同样定义在 Microsoft.Extensions.DependencyInjection.IServiceProvider 接口中。
也用于从服务容器中获取指定类型的服务。
如果容器中注册了该类型的服务,则返回对应的服务实例;如果没有注册,则抛出 InvalidOperationException 异常。
该方法确保所请求的服务必须存在,适用于确定服务应该已注册的场景,避免手动检查 null。
总结:
GetService 在服务不存在时返回 null,需自行处理;GetRequiredService 在服务不存在时抛出异常,更严格,适用于必须存在的服务。