Asp.Net Core 托管服务
文章目录
- 前言
- 一、说明
- 二、使用步骤
- 1.创建托管服务
- 方式一:继承 BackgroundService
- 方式二:直接实现 IHostedService
- 2.注册托管服务
- 3.处理作用域服务
- 4.使用定时器(System.Threading.Timer)
- 5.结合 Quartz.NET 实现复杂调度
- 三、. 注意事项
- 总结
前言
在ASP.NET Core中,托管服务(Hosted Services)允许你在后台运行长时间运行的任务,例如定时任务、消息队列处理、数据清理等。
一、说明
托管服务需要实现IHostedService接口,或者继承BackgroundService类。BackgroundService是ASP.NET Core提供的抽象类,可能更方便,因为它已经实现了部分接口方法。
二、使用步骤
1.创建托管服务
托管服务需实现 IHostedService 接口或继承 BackgroundService 类(推荐后者,因为它简化了实现)。
方式一:继承 BackgroundService
- MyBackgroundService.cs
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
- MyHostedService.cs
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.注册托管服务
- 在 Program.cs 中,通过 AddHostedService 将服务注册到依赖注入容器:
builder.Services.AddHostedService<MyBackgroundService>();
// 或
builder.Services.AddHostedService<MyHostedService>();
3.处理作用域服务
- 若托管服务需要访问作用域服务(如 DbContext),需通过 IServiceScopeFactory 创建作用域:
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)
- 托管服务通常结合定时器实现周期性任务:使用 BackgroundService + PeriodicTimer
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 实现复杂调度
- 对于复杂的定时任务,可集成第三方库(如 Quartz.NET)
- 安装必要的Nuget包
Install-Package Quartz.Extensions.Hosting
- 示例:
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;}} }
- 在Program.cs中添加Quartz注册服务
// 注册 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中实现后台任务的推荐方式,适用于:
- 定时任务(如数据同步、缓存刷新)
- 消息队列消费
- 资源监控
- 其他需要长时间运行的进程
通过 BackgroundService 或 IHostedService,结合依赖注入和异步编程模型,可以轻松构建可靠的后台任务。