Codebelt

Codebelt.Bootstrapper.Console

Pair a console program or minimal host with Codebelt's bootstrapper pipeline so RunAsync executes through IHostedService and participates in graceful shutdown.

.NET 10.0 / .NET 9.0 MIT v5.1.1 9,274 downloads

Overview

Codebelt.Bootstrapper.Console extends Codebelt.Bootstrapper with hosted console-app execution. The package supports the conventional ConsoleProgram<TStartup> plus ConsoleStartup pairing and a minimal-host alternative through MinimalConsoleProgram, while both models run RunAsync from an IHostedService and stop the application when the work completes.

The README positions the package for hosted console apps and services, and the functional tests exercise the lifecycle around successful execution, suppressed status messages, premature completion, thrown exceptions, and null startup or program activation. That combination makes this package the repository's console-specific layer on top of the lower-level bootstrapper abstractions.

Key APIs

ConsoleProgram<TStartup> is the conventional entry-point base class. Its protected CreateHostBuilder(string[] args) method creates the default host, applies bootstrapper environment defaults, registers the startup type, and then wires the console hosted service for that startup.

ConsoleStartup is the console-oriented startup abstraction. It keeps the IConfiguration and IHostEnvironment constructor contract and is the required type for both ConsoleProgram<TStartup> and UseConsoleStartup<TStartup>().

HostBuilderExtensions.UseConsoleStartup<TStartup>() adds ConsoleHostedService<TStartup> to the service collection. ConsoleHostedService<TStartup> subscribes to ApplicationStarted, calls RunAsync on the activated startup instance, requests shutdown when the task ends, and is tested against success, exception, premature-end, and null-activation scenarios.

MinimalConsoleProgram provides the same lifecycle for HostApplicationBuilder. Its protected builder factory calls Host.CreateApplicationBuilder(args), applies bootstrapper environment defaults, registers the selected program type through UseBootstrapperProgram(...), and then enables UseMinimalConsoleProgram().

MinimalConsoleProgram<TProgram> removes the need to pass a Type when the program is already known at compile time. The builder tests verify that both minimal-program entry points register IProgramFactory and MinimalConsoleHostedService.

HostApplicationBuilderExtensions.UseBootstrapperProgram(...) and UseMinimalConsoleProgram() are the minimal-host wiring points. The first registers ProgramFactory as IProgramFactory, and the second adds MinimalConsoleHostedService, which mirrors the conventional hosted-service lifecycle for minimal programs.

Basic usage

using System;
using System.Threading;
using System.Threading.Tasks;
using Codebelt.Bootstrapper.Console;
using Codebelt.Extensions.Xunit;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

namespace MyProject.Tests;

public class CodebeltBootstrapperConsoleTest : Test
{
    public CodebeltBootstrapperConsoleTest(ITestOutputHelper output) : base(output)
    {
    }

    [Fact]
    public async Task ShouldRunConsoleStartupThroughTheHost()
    {
        using var host = ReportingProgram.CreateHostBuilderAccessor([]).Build();
        var signal = host.Services.GetRequiredService<ExecutionSignal>();

        await host.StartAsync();
        await signal.Completed.Task.WaitAsync(TimeSpan.FromSeconds(5));
        await host.StopAsync();

        TestOutput.WriteLine($"RunAsync status: {signal.Status}");
        Assert.Equal("completed", signal.Status);
    }

    private sealed class ReportingProgram : ConsoleProgram<ReportingStartup>
    {
        public static IHostBuilder CreateHostBuilderAccessor(string[] args) => CreateHostBuilder(args);
    }

    private sealed class ReportingStartup : ConsoleStartup
    {
        public ReportingStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment)
        {
        }

        public override void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<ExecutionSignal>();
        }

        public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
        {
            var signal = serviceProvider.GetRequiredService<ExecutionSignal>();
            signal.Status = "completed";
            signal.Completed.TrySetResult(true);
            return Task.CompletedTask;
        }
    }

    private sealed class ExecutionSignal
    {
        public string Status { get; set; } = "pending";

        public TaskCompletionSource<bool> Completed { get; } = new(TaskCreationOptions.RunContinuationsAsynchronously);
    }
}

Usage guidance

Use this package when you want console work to run through the generic host instead of calling RunAsync manually from Main. The package is the right fit when you want either a Startup-driven console application or a minimal-host console program to share the same hosted-service lifecycle, logging, and shutdown behavior.

Choose Codebelt.Bootstrapper instead when you only need the underlying bootstrapper abstractions and not the console-specific hosted services from this package. Within this package, reach for ConsoleProgram<TStartup> when you want an explicit startup class and use MinimalConsoleProgram or MinimalConsoleProgram<TProgram> when HostApplicationBuilder is the better fit for your console entry point.

Family packages