- In ASP.NET Core, background tasks can be implemented as hosted services.
- A hosted service is a class with background task logic that implements the
IHostedService
interface.
IHostedService Interface
- Hosted services implement the
IHostedService
interface.
- The interface defines two methods for objects that are managed by the host:
- The hosted service is a singleton that's activated once at app startup and gracefully shutdown at app shutdown.
- When
IDisposable
is implemented, resources can be disposed when the service container is disposed.
- If an error is thrown during background task execution,
Dispose
should be called even if StopAsync
isn't called.
Timed Background Tasks
- A timed background task makes use of the System.Threading.Timer class.
- The timer triggers the task's
DoWork
method.
- The timer is disabled on
StopAsync
and disposed when the service container is disposed on Dispose
:
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working.");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
- The service is registered in
Startup.ConfigureServices
:
services.AddSingleton<IHostedService, TimedHostedService>();
Consuming a Scoped Service
- To use scoped services within an
IHostedService
, create a scope.
- No scope is created for a hosted service by default.
- The scoped background task service contains the background task's logic.
internal interface IScopedProcessingService
{
void DoWork();
}
internal class ScopedProcessingService : IScopedProcessingService
{
private readonly ILogger _logger;
public ScopedProcessingService(ILogger<ScopedProcessingService> logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.LogInformation("Scoped Processing Service is working.");
}
}
- The hosted service creates a scope to resolve the scoped background task service to call its
DoWork
method:
internal class ConsumeScopedServiceHostedService : IHostedService
{
private readonly ILogger _logger;
public ConsumeScopedServiceHostedService(IServiceProvider services,
ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is starting.");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation("Consume Scoped Service Hosted Service is working.");
using (var scope = Services.CreateScope())
{
var scopedProcessingService = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
scopedProcessingService.DoWork();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Consume Scoped Service Hosted Service is stopping.");
return Task.CompletedTask;
}
}