BetterHostedServices 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package BetterHostedServices --version 1.0.0                
NuGet\Install-Package BetterHostedServices -Version 1.0.0                
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="BetterHostedServices" Version="1.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BetterHostedServices --version 1.0.0                
#r "nuget: BetterHostedServices, 1.0.0"                
#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 BetterHostedServices as a Cake Addin
#addin nuget:?package=BetterHostedServices&version=1.0.0

// Install BetterHostedServices as a Cake Tool
#tool nuget:?package=BetterHostedServices&version=1.0.0                

Better Hosted Services

GitHub Actions Status GitHub Actions Build History

This projects is out to solve some limitations with ASP.NET Core's IHostedService and BackgroundService.

Problem 1. IHostedService is not good for long running tasks.

Creating an IHostedService with a long-running task, will delay application startup. A class like this will never let the application start.

public class MyHostedService: IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            // Do some stuff here
        }
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}

Problem 2. BackgroundServices fail silently if an error occurs

Microsoft recommends extending from BackgroundService for long running tasks. However BackgroundServices fails silently if an uncaught error occurs.

This example will not throw an error, but simply fail silently.

public class YieldingAndThenCrashingCriticalBackgroundService: CriticalBackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Yield(); // Or some other async work
        throw new Exception("Oh no something really bad happened");
    }
}

You'll simply never know this error happened. We can do better.

Problem 3. HostedServices are not part of the default DI container

If you want to interact with a HostedService from a controller or another service, you're out of luck. They're completely separate. There's no built-in way to get a reference to a running IHostedService

Introducing BetterHostedServices

BetterHostedServices is a tiny library (<200 lines of code not including tests) that solves some of these issues.

CriticalBackgroundService

It introduces a class you can inherit from: CriticalBackgroundService - if an uncaught error happens in a CriticalBackgroundService it will log it and stop the application

You can use it like this: Inherit from the CriticalBackgroundService

public class YieldingAndThenCrashingBackgroundService: BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Yield(); // Or some other async work
        throw new Exception("Oh no something really bad happened");
    }
}

Add AddBetterHostedServices() and your hosted service inside Startup.cs's ConfigureServices method.

services.AddBetterHostedServices();
services.AddHostedService<YieldingAndThenCrashingCriticalBackgroundService>();

That's it! Your CriticalBackgroundService now stops the application if an error happens

Customizing error handling

If you need to customize error logging or handle the error in another way, you can override the OnError method.

public class YieldingAndThenCrashingCriticalBackgroundService : CriticalBackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Yield(); // Hand over control explicitly, to ensure this behaviour also works
        throw new Exception("Crash after yielding");
    }

    protected override void OnError(Exception exceptionFromExecuteAsync)
    {
        // Custom logging here
        this._applicationEnder.ShutDownApplication(); // or simply call base.OnError
    }

    public YieldingAndThenCrashingCriticalBackgroundService(IApplicationEnder applicationEnder) : base(applicationEnder)
    {
    }
}

AddHostedServiceAsSingleton

Occasionally you might want to interact with a Hosted Service from the rest of your application. You can do this via the AddHostedServiceAsSingleton method on the IServiceCollection

services.AddHostedServiceAsSingleton<ISomeBackgroundService, SomeBackgroundService>();

After that, you can inject them via the DI container just like any ordinary singleton.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
2.0.3 39,310 9/14/2021
2.0.1 4,151 10/1/2020
2.0.0 200 10/1/2020
1.0.2 227 9/30/2020
1.0.0 457 9/13/2020