Asp.Net Core 托管服务

文章目录


前言

ASP.NET Core 中,托管服务(Hosted Services)允许你在后台运行长时间运行的任务,例如定时任务、消息队列处理、数据清理等。

一、说明

托管服务需要实现IHostedService 接口,或者继承BackgroundService 类。BackgroundServiceASP.NET Core提供的抽象类,可能更方便,因为它已经实现了部分接口方法。

二、使用步骤

1.创建托管服务

托管服务需实现 IHostedService 接口或继承 BackgroundService 类(推荐后者,因为它简化了实现)。

方式一:继承 BackgroundService

  1. MyBackgroundService.cs

    csharp 复制代码
    using HostedService.Data;
    using Microsoft.EntityFrameworkCore;
    
    namespace HostedService.Service
    {
        public class MyBackgroundService : BackgroundService
        {
            private readonly ILogger<MyBackgroundService> _logger;
            public MyBackgroundService(ILogger<MyBackgroundService> logger, IServiceScopeFactory scopeFactory)
            {
                _logger = logger;
            }
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                _logger.LogInformation("后台服务启动成功");
                while (!stoppingToken.IsCancellationRequested)
                {
                    _logger.LogInformation("开始导出数据。。。");
                    //处理数据导出
                    _logger.LogInformation("导出数据成功。。。");
                }
            }
        }
    }

方式二:直接实现 IHostedService

  1. MyHostedService.cs

    csharp 复制代码
    using Microsoft.Extensions.Hosting;
    using Microsoft.Extensions.Logging;
    using System.Threading;
    using System.Threading.Tasks;
    
    public class MyHostedService : IHostedService
    {
        private readonly ILogger<MyHostedService> _logger;
        private Timer _timer;
    
        public MyHostedService(ILogger<MyHostedService> logger)
        {
            _logger = logger;
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("托管服务已启动。");
            _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
            return Task.CompletedTask;
        }
    
        private void DoWork(object state)
        {
            _logger.LogInformation("执行定时任务...");
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("托管服务已停止。");
            _timer?.Dispose();
            return Task.CompletedTask;
        }
    }

2.注册托管服务

  1. 在 Program.cs 中,通过 AddHostedService 将服务注册到依赖注入容器:
csharp 复制代码
builder.Services.AddHostedService<MyBackgroundService>();
// 或
builder.Services.AddHostedService<MyHostedService>();

3.处理作用域服务

  1. 若托管服务需要访问作用域服务(如 DbContext),需通过 IServiceScopeFactory 创建作用域:

    csharp 复制代码
    using HostedService.Data;
    using Microsoft.EntityFrameworkCore;
    
    namespace HostedService.Service
    {
        public class MyBackgroundService : BackgroundService
        {
            private readonly ILogger<MyBackgroundService> _logger;
            private readonly IServiceScope _scope;
            public MyBackgroundService(ILogger<MyBackgroundService> logger, IServiceScopeFactory scopeFactory)
            {
                _logger = logger;
                _scope = scopeFactory.CreateScope();
            }
            public override void Dispose()
            {
                this._scope.Dispose();
                base.Dispose();
            }
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                var db=_scope.ServiceProvider.GetRequiredService<MyDbContext>();
                _logger.LogInformation("后台服务启动成功");
                while (!stoppingToken.IsCancellationRequested)
                {
                    _logger.LogInformation("开始导出数据。。。");
                    //使用db操作数据库并导出
                    long count=await db.Users.CountAsync();
                    await File.WriteAllTextAsync("d:/Hosted.txt",count.ToString());
                    await Task.Delay(5000);
                    _logger.LogInformation("导出数据成功。。。");
                }
    
            }
        }
    }

4.使用定时器(System.Threading.Timer)

  1. 托管服务通常结合定时器实现周期性任务:使用 BackgroundService + PeriodicTimer

    csharp 复制代码
    using HostedService.Data;
    using Microsoft.EntityFrameworkCore;
    
    namespace HostedService.Service
    {
        public class MyBackgroundService : BackgroundService
        {
            private readonly ILogger<MyBackgroundService> _logger;
            private readonly IServiceScope _scope;
            private PeriodicTimer? _timer;
            public MyBackgroundService(ILogger<MyBackgroundService> logger, IServiceScopeFactory scopeFactory)
            {
                _logger = logger;
                _scope = scopeFactory.CreateScope();
            }
            
            protected override async Task ExecuteAsync(CancellationToken stoppingToken)
            {
                _timer = new PeriodicTimer(TimeSpan.FromSeconds(5)); // 每5秒执行一次                                                                 
                var db = _scope.ServiceProvider.GetRequiredService<MyDbContext>();
                while (await _timer.WaitForNextTickAsync(stoppingToken))
                {
                    try
                    {
                        // 执行任务
                        await DoWorkAsync(db);
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "定时任务执行失败");
                    }
                    
                }
    
            }
            private async Task DoWorkAsync(MyDbContext db)
            {
                _logger.LogInformation("后台服务启动成功");            
                _logger.LogInformation("开始导出数据。。。");
                long count = await db.Users.CountAsync();
                await File.WriteAllTextAsync("d:/Hosted.txt", count.ToString());
                await Task.Delay(5000);
                _logger.LogInformation("导出数据成功。。。");
                await Task.CompletedTask;
            }
            public override void Dispose()
            {
                this._scope.Dispose();
                base.Dispose();
            }
            public override async Task StopAsync(CancellationToken stoppingToken)
            {
                _timer?.Dispose();
                await base.StopAsync(stoppingToken);
            }
        }
    }

5.结合 Quartz.NET 实现复杂调度

  1. 对于复杂的定时任务,可集成第三方库(如 Quartz.NET

  2. 安装必要的Nuget包

    csharp 复制代码
    Install-Package Quartz.Extensions.Hosting
  3. 示例:

    csharp 复制代码
    using HostedService.Data;
    using Microsoft.EntityFrameworkCore;
    using Quartz;
    using static Quartz.Logging.OperationName;
    
    namespace HostedService.Service
    {
        public class QuartzHostedService : IHostedService
        {
            private readonly ISchedulerFactory _schedulerFactory;
            
            public QuartzHostedService(ISchedulerFactory schedulerFactory)
            {
                _schedulerFactory = schedulerFactory;
            }
    
            private IScheduler _scheduler;
            public async Task StartAsync(CancellationToken cancellationToken)
            {
                _scheduler = await _schedulerFactory.GetScheduler(cancellationToken);
                var job = JobBuilder.Create<MyJob>().Build();
                var trigger = TriggerBuilder.Create()
                    .StartNow()
                    .WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
                    .Build();
                await _scheduler.ScheduleJob(job, trigger, cancellationToken);
                await _scheduler.Start(cancellationToken);
            }
    
            public Task StopAsync(CancellationToken cancellationToken)
            {
                throw new NotImplementedException();
            }
        }
        public class MyJob : IJob
        {
            private readonly IServiceScope _scope;
            private readonly ILogger<MyJob> _logger;
    
            public MyJob(ILogger<MyJob> logger, IServiceScopeFactory scopeFactory)
            {
                _logger = logger;
                _scope = scopeFactory.CreateScope();
            }
    
            public async Task Execute(IJobExecutionContext context)
            {
                var db = _scope.ServiceProvider.GetRequiredService<MyDbContext>();
                _logger.LogInformation("后台服务启动成功");
                _logger.LogInformation("开始导出数据。。。");
                long count = await db.Users.CountAsync();
                await File.WriteAllTextAsync("d:/Hosted.txt", count.ToString());
                await Task.Delay(5000);
                _logger.LogInformation("导出数据成功。。。");
                await Task.CompletedTask;
                Console.WriteLine("Quartz 任务执行中...");
                await Task.CompletedTask;
            }
        }
    }
  4. Program.cs 中添加Quartz 注册服务

    csharp 复制代码
    // 注册 Quartz
    builder.Services.AddQuartz(q =>
    {
        // 添加作业和触发器
        var jobKey = new JobKey("MyJob");
        q.AddJob<MyJob>(opts => opts.WithIdentity(jobKey));
        q.AddTrigger(opts => opts
            .ForJob(jobKey)
            .WithCronSchedule("0/10 * * * * ?")
        );
    });
    
    // 添加托管服务
    builder.Services.AddQuartzHostedService();

三、. 注意事项

  • 生命周期:托管服务默认是单例的,避免在构造函数中注入作用域服务。
  • 优雅关闭 :正确处理 CancellationToken,确保任务能及时停止。
  • 异步操作 :避免在 ExecuteAsync 中使用阻塞操作,优先使用异步方法。
  • 异常处理:捕获并记录异常,防止服务因未处理异常而终止。

总结

  • 托管服务是ASP.NET Core中实现后台任务的推荐方式,适用于:

    • 定时任务(如数据同步、缓存刷新)
    • 消息队列消费
    • 资源监控
    • 其他需要长时间运行的进程

    通过 BackgroundServiceIHostedService,结合依赖注入和异步编程模型,可以轻松构建可靠的后台任务。

相关推荐
幽络源小助理1 天前
springboot校园车辆管理系统源码 – SpringBoot+Vue项目免费下载 | 幽络源
vue.js·spring boot·后端
刀法如飞1 天前
一款开箱即用的Spring Boot 4 DDD工程脚手架
java·后端·架构
uzong1 天前
后端系统设计文档模板
后端
幽络源小助理1 天前
SpringBoot+Vue车票管理系统源码下载 – 幽络源免费项目实战代码
vue.js·spring boot·后端
uzong1 天前
软件架构指南 Software Architecture Guide
后端
又是忙碌的一天1 天前
SpringBoot 创建及登录、拦截器
java·spring boot·后端
勇哥java实战分享1 天前
短信平台 Pro 版本 ,比开源版本更强大
后端
学历真的很重要1 天前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
计算机毕设VX:Fegn08951 天前
计算机毕业设计|基于springboot + vue二手家电管理系统(源码+数据库+文档)
vue.js·spring boot·后端·课程设计
上进小菜猪1 天前
基于 YOLOv8 的智能杂草检测识别实战 [目标检测完整源码]
后端