Popular blog tags
One of the most common uses for a Console Application is to build what is known as headless services, this is particulary common when working with Docker containers and Kubernetes (K8S).Using headless services, it is possible to create a service grouping that does not allocate an IP address or forward traffic, enabling you to definitively control what specific pods you connect and communicate with.We can develop headless services using .net core console applications by making use of the .net core generic host. What is .net core generic host A common scenario or use case for headless services is to run in a Fire And Forget asynchronous pattern, which facilitate background processing tasks, perhaps handling messages on a queue for example, common in a cloud native, container based architectures. The goal of the Generic Host is to decouple the HTTP pipeline from the Web Host API to enable a wider array of host scenarios. The Generic Host is new in ASP.NET Core 2.1 and isn’t suitable for web hosting, but is ideally used for Console Applications which are typically used to develop headless services. The Generic Host library is available in Microsoft.Extensions.Hosting namespace and provided by Microsoft.Extensions.Hosting package. To add the namespace using the .net core CLI   1 dotnet add package Microsoft.Extensions.Hosting   Introducing IHost and the HostBuilder In .NET Core 2.1 Generic Host enables developers easily set up cross-cutting concerns for non-web based applications including: Configuration Logging Dependency Injection (DI) The IHost interface offers a number of benefits and features: Graceful shut down Dependency Injection Logging Configuration These features are particularly important when developing complex data processing tasks in Docker containers. Especially graceful shut down, as it helps to keep application state consistent. Implement IHost in .net core console applications The IHost interface and the HostBuilder class provide similar experience to the respective IWebHost and WebHostBuilder .net core web developers are familiar with when using ASP.net core. IHost and HostBuilder are components of a new feature set of .NET Core 2.1, to simplify the creation of console based services by providing a pattern for adding cross-cutting concerns such as dependency injection, configuration and logging. You need to bear in mind that applications which will implement the IHost interface will typically be required to run asynchronously. This will usually require marking the Main method as async.   1 2 3 4 5 6 7 public static async Task Main(string[] args) {     var host = new HostBuilder()         .Build();       await host.RunAsync(); }   If you’ve generated your Console Application using the dotnet new console you may need to update your project file to include   1 2 3 4 5 <PropertyGroup>     <LangVersion>latest</LangVersion>     <OutputType>Exe</OutputType>     <TargetFramework>netcoreapp2.1</TargetFramework>   </PropertyGroup>   In order to continue developing our application we will need to add several more package references   1 2 3 4 5 6 7 8 dotnet add package Microsoft.Extensions.Configuration.CommandLine     dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables     dotnet add package Microsoft.Extensions.Configuration.Json     dotnet add package Microsoft.Extensions.Hosting     dotnet add package Microsoft.Extensions.Hosting.Abstractions     dotnet add package Microsoft.Extensions.Logging     dotnet add package Microsoft.Extensions.Logging.Configuration     dotnet add package Microsoft.Extensions.Logging.Console   HostBuilder does not provide an extension method enabling the use of a StartUp class The HostingHostBuilder provides extension methods to configure host services. The following extension available: ConfigureAppConfiguration – Application Configuration ConfigureContainer – Configure the instantiated dependency injection container ConfigureLogging – Configure Logging ConfigureServices – Adds services to the Dependency Injection container RunConsoleAsync – Enables console support UseConsoleLifetime – Listens for shutdown signals UseContentRoot – Specify the content root directory UseEnvironment – Specify the environment The RunConsoleAsync will start services and wait on an exit signal in the application. Building a Host Using the Main method create a HostBuilder and use extension methods to register services with DI, read configuration and configure the logging for your application. To configure the Host we will use ConfigureHostConfiguration to initialize the IHostingEnvironmentfor use later in the build process. Environment variable configuration isn’t added by default, so we have to call AddEnvironmentVariables on the host builder to configure the host from environment variables. AddEnvironmentVariables accepts an optional user-defined prefix, by convention use your application name in our example we’ve declared a constant string value with _prefix set with the application name. Use the ConfigureAppConfiguration configuration providers to construct the final representation of configuration values for our application. In the example below we read the configuration values from an appsettings.json file first, followed by environment variables and finally from any arguments passed into the application. ConfigureServices is where we configure the services we want our application to run, registering services with the ServiceCollection. Registration is performed using extension methods on the ServiceCollection and once complete, enabling DI in our application.   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 class Program     {         private const string _prefix = "FUZZBIZZ_";         private const string _appsettings = "appsettings.json";         private const string _hostsettings = "hostsettings.json";         public static async Task Main(string[] args)         {             var host = new HostBuilder()                 .ConfigureHostConfiguration(configHost =>                 {                     configHost.SetBasePath(Directory.GetCurrentDirectory());                     configHost.AddJsonFile(_hostsettings, optional: true);                     configHost.AddEnvironmentVariables(prefix: _prefix);                     configHost.AddCommandLine(args);                 })                 .ConfigureAppConfiguration((hostContext, configApp) =>                 {                     configApp.SetBasePath(Directory.GetCurrentDirectory());                     configApp.AddJsonFile(_appsettings, optional: true);                     configApp.AddJsonFile(                         $"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json",                         optional: true);                     configApp.AddEnvironmentVariables(prefix: _prefix);                     configApp.AddCommandLine(args);                 })                 .ConfigureServices((hostContext, services) =>                 {                     services.AddLogging();                     services.Configure<Application>(hostContext.Configuration.GetSection("application"));                     services.AddHostedService<FizzBuzzHostedService>();                   })                 .ConfigureLogging((hostContext, configLogging) =>                 {                     configLogging.AddConsole();                   })                 .UseConsoleLifetime()                 .Build();               await host.RunAsync();         }         }   Using IHostedService The actual implementation of the service is defined using IHostedService interface. When an application is started, it will call StartAsync method. At shutdown, StopAsync is called and the service cleans up a little before the application is killed. IHostedService implementation defined and registered with DI container using ConfigureServices Usually in Linux daemons, your service implementation will be a Singleton class – only one instance of the service for the application lifetime. It also adds the configuration POCO (Plain Old C# Object) to the the services for dependency injection. This implements the IOption interface in .NET Core, which can take a type that it will attempt to bind CLI parameters and environment variables to based on the field name.     1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using System.Threading;     using System.Threading.Tasks;     using Microsoft.Extensions.Hosting;     using Microsoft.Extensions.Logging;     using Microsoft.Extensions.Options;     public class SampleService : IHostedService     {         public Task StartAsync(CancellationToken cancellationToken)         {             throw new System.NotImplementedException();         }           public Task StopAsync(CancellationToken cancellationToken)         {             throw new System.NotImplementedException();         }     }     .