[go: up one dir, main page]

DEV Community

Cover image for Modern minimal workers in .NET
Anthony Simmon
Anthony Simmon

Posted on • Edited on • Originally published at anthonysimmon.com

Modern minimal workers in .NET

Since the release of .NET 6, we've heard a lot about ASP.NET Core minimal APIs.
They've been discussed at Microsoft conferences, in blog posts, in YouTube videos, and on social networks.
We've all seen this kind of code sample of a minimal API:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");
app.Run();
Enter fullscreen mode Exit fullscreen mode

This is significantly more concise than controller-based web APIs, and we can all agree on that.
But did you know that since the release of .NET 7, there is also a way to create minimal workers?

A minimal worker console application looks like this:

var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<MyWorkerService>();

var app = builder.Build();
app.Run();
Enter fullscreen mode Exit fullscreen mode

Creating a minimal worker in .NET

Create an empty console application and reference the Microsoft.Extensions.Hosting package, at least version 7.0.0 (Host.CreateApplicationBuilder() has been added in this version).

You can also reference the Microsoft.NET.Sdk.Worker SDK in your project file, which is actually a dependency of the Microsoft.NET.Sdk.Web SDK, as mentioned in the project's README.
Referencing the Microsoft.NET.Sdk.Worker provides some benefits such as:

  • *.json (like appsettings.json) files are automatically copied to the output and publish directories.
  • A few Microsoft.Extensions.* using directives are automatically added only if you enable implicit usings.

So your project file should look like this:

<Project Sdk="Microsoft.NET.Sdk.Worker">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/> <!-- or later -->
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

You can even target .NET Framework:

<Project Sdk="Microsoft.NET.Sdk.Worker">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net462</TargetFramework>
    <LangVersion>10</LangVersion> <!-- required for top-level statements on .NET Framework -->
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.0"/> <!-- or later -->
  </ItemGroup>
</Project>
Enter fullscreen mode Exit fullscreen mode

Host.CreateApplicationBuilder() returns aHostApplicationBuilder, which shares some similarities with the WebApplicationBuilder used in ASP.NET Core minimal APIs.
You are probably already familiar with these properties:

Here’s an example where we use all of these properties:

var builder = Host.CreateApplicationBuilder(args);

if (!builder.Environment.IsDevelopment())
{
    builder.Configuration.AddAzureKeyVault(new Uri("https://mykeyvault"), new DefaultAzureCredential());
}

builder.Logging.AddSystemdConsole();

builder.Services.AddHangfire(/* [...] */);
builder.Services.AddHangfireServer();

/* [...] */

builder.Build().Run();
Enter fullscreen mode Exit fullscreen mode

Next, register your background service(s) using builder.Services.AddHostedService<YourConcreteBackgroundServiceType>().
The background service type must implement IHostedService or be a subclass of BackgroundService.
You can learn more about background services here.

Keep in mind that if you host long-running services in your worker, you should consider enabling server garbage collection for performance implications.
You can learn more about this topic on this documentation page about server garbage collection.

<PropertyGroup>
  <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
Enter fullscreen mode Exit fullscreen mode

Let's wrap this up with a final tip.
If you want to create a short-lived console application that leverages the full potential of minimal workers and .NET extensions libraries, you can create a BackgroundService that calls IHostApplicationLifetime.StopApplication() when the work is done:

internal sealed class MyShortLivedService : BackgroundService
{
    private readonly IHostApplicationLifetime _applicationLifetime;

    public MyShortLivedService(IHostApplicationLifetime applicationLifetime)
    {
        this._applicationLifetime = applicationLifetime;
    }

    protected override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // [...]
        // Do some work then shut down the application
        this._applicationLifetime.StopApplication();
        return Task.CompletedTask;
    }
}
Enter fullscreen mode Exit fullscreen mode

References

Top comments (0)