Atc.Hosting
1.0.21
See the version list below for details.
dotnet add package Atc.Hosting --version 1.0.21
NuGet\Install-Package Atc.Hosting -Version 1.0.21
<PackageReference Include="Atc.Hosting" Version="1.0.21" />
paket add Atc.Hosting --version 1.0.21
#r "nuget: Atc.Hosting, 1.0.21"
// Install Atc.Hosting as a Cake Addin #addin nuget:?package=Atc.Hosting&version=1.0.21 // Install Atc.Hosting as a Cake Tool #tool nuget:?package=Atc.Hosting&version=1.0.21
Atc.Hosting
The Atc.Hosting namespace serves as a toolbox for building scalable and reliable hosting solutions, with an emphasis on background services. It contains classes and extension methods designed to handle common hosting scenarios, providing enhanced features like custom logging, retries, and advanced configuration options. The namespace aims to streamline development efforts and improve the maintainability of hosted applications.
Table of Contents
- Atc.Hosting
- Table of Contents
- BackgroundServiceBase
<T>
- BackgroundServiceHealthService
- Complete TimeFileWorker example
- Extensions for ServiceProvider
- Requirements
- How to contribute
BackgroundServiceBase<T>
The BackgroundServiceBase<T>
class serves as a base for background services that require enhanced features like custom logging, retries, and configurable service options. It extends the ASP.NET Core's BackgroundService
class, providing a more robust framework for handling
background tasks.
Features
Logging
- Utilizes
ILogger<T>
for type-specific, high-performance logging. - Automatically enriches log entries with the name of the service type (
T
).
Retry Mechanism
- Built-in retries using the
Polly
library. - Retry count and exponential backoff settings are configurable.
Error Handling
- Catches exceptions and logs them with a severity of
LogLevel.Warning
. - Designed to log errors rather than crashing the service.
Configuration Options
- Allows for startup delays.
- Configurable retry count.
- Configurable repeat interval for running tasks.
Ease of Use
- Simple to derive from and implement your work in the
DoWorkAsync
method.
Sample Usage
public class MyBackgroundService : BackgroundServiceBase<MyBackgroundService>
{
public MyBackgroundService(ILogger<MyBackgroundService> logger, IBackgroundServiceOptions options)
: base(logger, options)
{
}
public override Task DoWorkAsync(CancellationToken stoppingToken)
{
// Your background task logic here
}
}
Setup BackgroundService via Dependency Injection
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.Build();
var host = Host
.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddSingleton<ITimeService, TimeService>();
services.Configure<TimeFileWorkerOptions>(configuration.GetSection(TimeFileWorkerOptions.SectionName));
services.AddHostedService<TimeFileWorker>();
})
.Build();
host.Run();
In this example the TimeFileWorker
BackgroundService is wired up by using AddHostedService<T>
as a normal BackgroundService
.
Note: TimeFileWorker
uses TimeFileWorkerOptions
that implements IBackgroundServiceOptions
.
BackgroundServiceHealthService
IBackgroundServiceHealthService
is an interface that provides methods to manage and monitor the health of background services in a .NET application.
Methods
SetMaxStalenessInSeconds(string serviceName, ushort seconds)
: Set the maximum allowed staleness duration for a service in seconds.SetRunningState(string serviceName, bool isRunning)
: Update the running state of a service.IsServiceRunning(string serviceName)
: Check if a service is running or not.
Setup via Dependency Injection
Include the following code snippet in your startup to wire up the BackgroundService incl. HealthService.
var host = Host
.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
//...existing dependency injection
services.AddSingleton<IBackgroundServiceHealthService, BackgroundServiceHealthService>(s =>
{
var healthService = new BackgroundServiceHealthService(s.GetRequiredService<ITimeProvider>());
var timeFileWorkerOptions = s.GetRequiredService<IOptions<TimeFileWorkerOptions>>().Value;
healthService.SetMaxStalenessInSeconds(nameof(TimeFileWorker), timeFileWorkerOptions.RepeatIntervalSeconds);
return healthService;
});
})
.Build();
Here, the maximum staleness for TimeFileWorker
is set using its RepeatIntervalSeconds
.
Using in Background Services
You can utilize IBackgroundServiceHealthService
within your background services as shown below.
Constructor dependency injection
public TimeFileWorker(
//...other parameters
IBackgroundServiceHealthService healthService,
//...other parameters
)
{
this.healthService = healthService;
//...other initializations
}
In your StartAsync
and StopAsync
methods, update the service's running state
public override async Task StartAsync(CancellationToken cancellationToken)
{
await base.StartAsync(cancellationToken);
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: true);
//...other code
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
//...other code
await base.StopAsync(cancellationToken);
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: false);
}
Inside your worker method, you can set the running state of the service (to update latest timestamp)
public override async Task DoWorkAsync(CancellationToken stoppingToken)
{
//...other code
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: true);
}
Complete TimeFileWorker example
A sample reference implementation can be found in the sample project Atc.Hosting.TimeFile.Sample
which shows an example of the service TimeFileWorker
that uses BackgroundServiceBase
and the IBackgroundServiceHealthService
.
public class TimeFileWorker : BackgroundServiceBase<TimeFileWorker>
{
private readonly IBackgroundServiceHealthService healthService;
private readonly ITimeProvider timeProvider;
private readonly TimeFileWorkerOptions workerOptions;
public TimeFileWorker(
ILogger<TimeFileWorker> logger,
IBackgroundServiceHealthService healthService,
ITimeProvider timeProvider,
IOptions<TimeFileWorkerOptions> workerOptions)
: base(
logger,
workerOptions.Value)
{
this.healthService = healthService;
this.timeProvider = timeProvider;
this.workerOptions = workerOptions.Value;
}
public override async Task StartAsync(
CancellationToken cancellationToken)
{
await base.StartAsync(cancellationToken);
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: true);
}
public override async Task StopAsync(
CancellationToken cancellationToken)
{
await base.StopAsync(cancellationToken);
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: false);
}
public override async Task DoWorkAsync(
CancellationToken stoppingToken)
{
var isServiceRunning = healthService.IsServiceRunning(nameof(TimeFileWorker));
Directory.CreateDirectory(workerOptions.OutputDirectory);
var time = timeProvider.UtcNow;
var outFile = Path.Combine(
workerOptions.OutputDirectory,
$"{time:yyyy-MM-dd--HHmmss}-{isServiceRunning}.txt");
await File.WriteAllTextAsync(outFile, $"{ServiceName}-{isServiceRunning}", stoppingToken);
healthService.SetRunningState(nameof(TimeFileWorker), isRunning: true);
}
}
Extensions for ServiceProvider
Defined extensions methods for ServiceProvider:
GetHostedService
<T>
Using GetHostedService<T>
Example on how to retrieve the BackgroundService from the HttpContext in a MVC controller or MinimalApi endpoint.
In this example we are working with the TimeFileWorker
BackgroundService.
Note: Remember to wire up BackgroundService in Program.cs
by adding this line services.AddHostedService<TimeFileWorker>();
.
Example setup for a MVC controller:
[HttpGet("my-method")]
public void GetMyMethod()
{
var timeFileWorker = httpContext.RequestServices.GetHostedService<TimeFileWorker>();
if (timeFileWorker is not null)
{
// Here we have access to the TimeFileWorker instance.
}
}
Example setup for a MinimalApi endpoint:
public void DefineEndpoints(WebApplication app)
{
app.MapGet("my-method", async httpContext =>
{
var timeFileWorker = httpContext.RequestServices.GetHostedService<TimeFileWorker>();
if (timeFileWorker is not null)
{
// Here we have access to the TimeFileWorker instance.
}
await Task.CompletedTask;
});
}
Requirements
How to contribute
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net7.0 is compatible. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
-
net7.0
- Atc (>= 2.0.349)
- Microsoft.Extensions.Hosting.Abstractions (>= 7.0.0)
- Microsoft.Extensions.Logging.Abstractions (>= 7.0.1)
- Polly (>= 7.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.