The core abstract class that provides the foundation for all hosted services.
public abstract class ClearHostedService : IHostedService, IDisposableprotected IServiceProvider ServiceProvider { get; }Gets the isolated service provider for this hosted service instance. Use this to resolve dependencies registered in RegisterDependencyInjection.
Example:
var myService = ServiceProvider.GetRequiredService<IMyService>();protected ILogger Logger { get; }Gets the Serilog logger instance configured for this hosted service. Use this for all logging operations.
Example:
Logger.Information("Processing started");
Logger.Error(ex, "An error occurred");protected virtual void RegisterDependencyInjection(IServiceCollection services)Override this method to register application-specific dependencies into the service collection.
Parameters:
services: The service collection to register dependencies into
Example:
protected override void RegisterDependencyInjection(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
services.AddSingleton<ICache, MemoryCache>();
}Notes:
- Called once during service startup, before
ExecuteAsync - Services registered here are isolated to this hosted service instance
- Common services (logging, configuration) are already registered by the base class
protected virtual void ConfigureLogging(ILoggingBuilder builder)Override this method to customize the logging configuration.
Parameters:
builder: The logging builder to configure
Example:
protected override void ConfigureLogging(ILoggingBuilder builder)
{
base.ConfigureLogging(builder);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.WriteTo.File("logs/myservice-.txt", rollingInterval: RollingInterval.Day)
.CreateLogger();
}Notes:
- Call
base.ConfigureLogging(builder)to include default configuration - Default configuration includes Console and File sinks
- ApplicationInsights is configured if instrumentation key is provided
protected virtual Task OnStartingAsync(CancellationToken cancellationToken)Override this method to perform initialization logic before ExecuteAsync begins.
Parameters:
cancellationToken: A cancellation token that can be used to cancel the startup operation
Returns:
- A task representing the asynchronous operation
Example:
protected override async Task OnStartingAsync(CancellationToken cancellationToken)
{
Logger.Information("Performing startup validation...");
var config = ServiceProvider.GetRequiredService<IConfiguration>();
ValidateConfiguration(config);
await WarmUpCacheAsync(cancellationToken);
}Notes:
- Called after dependency injection is configured
- Called before
ExecuteAsyncstarts - Exceptions thrown here will prevent the service from starting
protected virtual Task OnStoppingAsync(CancellationToken cancellationToken)Override this method to perform cleanup logic during service shutdown.
Parameters:
cancellationToken: A cancellation token that can be used to cancel the shutdown operation
Returns:
- A task representing the asynchronous operation
Example:
protected override async Task OnStoppingAsync(CancellationToken cancellationToken)
{
Logger.Information("Flushing pending operations...");
var messageQueue = ServiceProvider.GetRequiredService<IMessageQueue>();
await messageQueue.FlushAsync(cancellationToken);
Logger.Information("Cleanup completed");
}Notes:
- Called after
ExecuteAsynchas stopped - Called before resources are disposed
- Should complete quickly to avoid delaying shutdown
protected abstract Task ExecuteAsync(CancellationToken stoppingToken)Implement this method to define the main execution logic for your hosted service.
Parameters:
stoppingToken: A cancellation token that is triggered when the service should stop
Returns:
- A task representing the asynchronous operation
Example:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await DoWorkAsync(stoppingToken);
await Task.Delay(TimeSpan.FromSeconds(60), stoppingToken);
}
}Notes:
- This method runs on a background thread
- Should respect the
stoppingTokento enable graceful shutdown - Exceptions thrown here will be logged but won't crash the application
- For continuous operation, use a while loop with cancellation token checks
public Task StartAsync(CancellationToken cancellationToken)?? Do not override this method. It is implemented by the base class to orchestrate the startup sequence.
Startup Sequence:
- Configure logging
- Build service collection
- Call
RegisterDependencyInjection - Build service provider
- Call
OnStartingAsync - Start
ExecuteAsyncon background thread
public Task StopAsync(CancellationToken cancellationToken)?? Do not override this method. It is implemented by the base class to orchestrate the shutdown sequence.
Shutdown Sequence:
- Signal cancellation token
- Wait for
ExecuteAsyncto complete (with timeout) - Call
OnStoppingAsync - Dispose service provider
- Log completion
public void Dispose()Disposes of resources used by the hosted service. Called automatically by the .NET host.
Notes:
- Disposes the service provider
- Disposes any other managed resources
- Do not call this method directly
Defines lifecycle hooks for hosted services.
public interface IHostedServiceLifecycle
{
Task OnStartingAsync(CancellationToken cancellationToken);
Task OnStoppingAsync(CancellationToken cancellationToken);
}Defines the contract for dependency registration.
public interface IServiceRegistration
{
void RegisterDependencies(IServiceCollection services);
}Configuration options for logging.
public class LoggingOptions
{
public string LogLevel { get; set; } = "Information";
public string LogDirectory { get; set; } = "logs";
public RollingInterval RollingInterval { get; set; } = RollingInterval.Day;
public bool EnableConsoleLogging { get; set; } = true;
public bool EnableFileLogging { get; set; } = true;
public bool EnableApplicationInsights { get; set; } = false;
public string? ApplicationInsightsInstrumentationKey { get; set; }
}Configuration options for hosted services.
public class HostedServiceOptions
{
public TimeSpan ShutdownTimeout { get; set; } = TimeSpan.FromSeconds(30);
public bool EnableDetailedErrors { get; set; } = false;
}Base exception for hosted service-related errors.
public class HostedServiceException : Exception
{
public HostedServiceException(string message) : base(message) { }
public HostedServiceException(string message, Exception innerException)
: base(message, innerException) { }
}Thrown when there's an error during service registration.
public class ServiceRegistrationException : HostedServiceException
{
public ServiceRegistrationException(string message) : base(message) { }
public ServiceRegistrationException(string message, Exception innerException)
: base(message, innerException) { }
}protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Your work here
await Task.Delay(1000, stoppingToken);
}
}using var scope = ServiceProvider.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();Logger.Information("Processing {RecordCount} records for {CustomerId}", count, customerId);try
{
await ProcessDataAsync(stoppingToken);
}
catch (TransientException ex)
{
Logger.Warning(ex, "Transient error, will retry");
// Retry logic
}
catch (Exception ex)
{
Logger.Error(ex, "Fatal error in processing");
throw;
}protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var httpClient = new HttpClient();
// Use httpClient
}