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