Atc.Hosting 1.0.21

There is a newer version of this package available.
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                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Atc.Hosting" Version="1.0.21" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Atc.Hosting --version 1.0.21                
#r "nuget: Atc.Hosting, 1.0.21"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// 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                

NuGet Version

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

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

Contribution Guidelines

Coding Guidelines

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.1.0 511 10/7/2024
1.0.53 697 7/20/2024
1.0.46 360 4/12/2024
1.0.33 1,070 9/29/2023
1.0.21 320 9/7/2023
1.0.18 753 7/26/2023
1.0.15 779 4/14/2023
1.0.8 221 4/11/2023