Notes on dotNET Generic Host

Preface

This is a boring log during the learing process of C# and dotNET. This log may contain fact error and opinioned idea. And mostly a compilation of "Marcohard" docs and APIs.

References

The reason of the existence of this section is that: "Marcohard" docs are too scattered, thus the writer manually searched the useful part.

.NET fundamentals documentation

.NET Tools documentation

Advanced .NET programming documentation

.NET security documentation

.NET data access documentation

C# language reference

C# documentation

Brief Introduction

What is host?

A host is an object that encapsulates an app's resources and lifetime functionality, such as:

Why to use it?

Because it provides a robust and flexible framework for building and managing long-running applications. Plus that it already include logging integration and configuration management.

CreateApplicationBuilder vs CreateDefaultBuilder

Tl;dr: use CreateApplicationBuilder

See:

Offical explaination

Stackoverflow's question about this

Basic Setup

Create Project

dotnet new worker -n "MyProject"

Useful Project Settings

Edit MyProject.csproj:

<PropertyGroup>
    <AnalysisMode>All</AnalysisMode>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

NOT full list of PropertyGroup

ServerGarbageCollection Explaniation

AnalysisMode and EnforceCodeStyleInBuild Explaniation

C# compiler options

C# runtime configurations

Coding?

Now we have generated a Worker project. And some important files are:

Main structure

In general, what we (actual, template provided) do is to create a builder and add "resources" (such as services) to it. After doing this, we build host and run it. We will focus on what can be add to builder and how.

Overview

Given the API that:

HostApplicationBuilder API

We observed that HostApplicationBuilder has few methods and has little value on normal use case. So we focus on five properties:

Both Environment and Metrics are boring, see documentation instead.

Logging

Currently the best practice is as follows:

[LoggerMessage(level: LogLevel.Information, message: "Meow: {saying}")]
private partial void Meow(string saying);

And can be configured at runtime with appsettings.json

Offcial documentation about how to configure logging

Configuration

It seems that the option pattern is better than the naive method.

Define MyConfigSection1

#pragma warning disable CA1812
internal sealed record MyOptions
#pragma warning restore CA1812
{
    [Required]
    public required string Saying { get; set; }
}

DataAnnotations API Documenatation

P.S.: Why disable CA1812? Because we registered the class in an container and the analyzer cannot infer this usage. See:

Offical description about this warning and why we can suppress it

In Program.cs:

builder.Services.AddOptions<MyOptions>().Bind(builder.Configuration.GetSection(nameof(MyOptions)));

On difference of AddOptions and Configure:

Offical repo issue about this question

In our service:

#pragma warning disable CA1812
internal sealed partial class MyService(ILogger<MyService> logger, IOptions<MyOptions> options) : BackgroundService
#pragma warning restore CA1812

In appsettings.json

{
  "MyOptions": {
    "Saying": "Meow!"
  }
}

If we want use AoT in our project, we need to add an extra option to mitigate warning:

<PublishAot>True</PublishAot>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>

Services

This is a IServiceCollection. The writer will list some of them which are considered as interesting and convenient ones here.

For detailed, see API documentation:

IServiceCollection API



You've reached the end of this page. And you may Go to index or visit my friends.
About me and contacts
Except where otherwise noted, this site is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License