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);
        }
    }
}

image

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 在服务不存在时抛出异常,更严格,适用于必须存在的服务。

posted @ 2025-09-01 13:52  【唐】三三  阅读(10)  评论(0)    收藏  举报