3

Consider this extremely simple .NET Core 3.1 (and .NET 5) application with no special config or hosted services:

using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

internal class Program
{
    public static async Task Main(string[] args)
    {
        var builder = Host.CreateDefaultBuilder(args);
        builder.UseWindowsService();
        var host = builder.Build();

        var fireAndforget = Task.Run(async () => await host.RunAsync());
        await Task.Delay(5000);
        await host.StopAsync();
        await Task.Delay(5000);
        await host.RunAsync();
    }

The first Run (sent as a background fire and forget task only for the purpose of this test) and Stop complete successfully. Upon calling Run a second time, I receive this exception:

System.AggregateException : 'Object name: 'EventLogInternal'.Cannot access a disposed object. Object name: 'EventLogInternal'.)'

If I do the same but using StartAsync instead of RunAsync (this time no need for a fireAndForget), I receive a System.OperationCanceledException upon called StartAsync the second time.

Am I right to deduce that .NET Generic Host aren't meant to be stopped and restarted?

Why do I need this?

My goal is to have a single application running as a Windows Service that would host two different .NET Generic Host. This is based on recommendation from here in order to have separate configuration and dependency injection rules and message queues.

One would stay active for all application lifetime (until the service is stopped in the Windows services) and would serve as a entry point to receive message events that would start/stop the other one which would be the main processing host with full services. This way the main services could be in "idle" state until they receive a message triggering their process, and another message could return them to idle state.

Dunge
  • 532
  • 3
  • 19

2 Answers2

1

The host returned by CreateDefaultBuilder(...).Build() is meant to represent the whole application. From docs:

The main reason for including all of the app's interdependent resources in one object is lifetime management: control over app startup and graceful shutdown.

The default builder registers many services in singleton scope and when the host is stopped all of these services are disposed or switched to some "stopped" state. For example before calling StopAsync you can resolve IHostApplicationLifetime:

var appLifetime = host.Services.GetService<IHostApplicationLifetime>();

It has cancellation tokens representing application states. When you call StartAsync or RunAsync after stopping, all tokens still have IsCancellationRequested set to true. That's why the OperactionCancelledException is thrown in Host.StartAsync.

You can list other services during configuration: enter image description here

For me it sounds like you just need some background jobs to process messages but I've never used NServiceBus so I don't know how it will work with something like Hangfire. You can also implement IHostedService and use it in the generic host builder.

mcbr
  • 731
  • 5
  • 6
  • Thank you. So the answer is practically "No, and IHost is not meant to be stopped and restarted within an application lifetime". My problem comes from the usage of the library I use, which instead of supplying a service in ConfigureService is done via an extension method on IHostBuilder. I will follow up with them instead. – Dunge Nov 02 '20 at 19:45
  • Yeah, short answer is "No" :). I just wasn't sure about that and checked the source code. Technically you could build the host every time you need it but this sounds like an overkill. – mcbr Nov 02 '20 at 20:40
  • 1
    FYI: Even building a new host every time crash with the `EventLogInternal` exception at the second Stop. Same when triggering an application close with ctrl+c. They really don't seem to allow to use multiple IHost in the same application. – Dunge Nov 03 '20 at 17:09
0

I'm doing something like:

do
{
    using IHost host = BuildHost();
    await host.RunAsync();
} while (MainService.Restart);

with MainService constructor:

public MainService(IHostApplicationLifetime HostApplicationLifetime)

MainService.Restart is a static bool set by the MainService itself in response to some event which also calls HostApplicationLifetime.StopApplication().

Jck
  • 61
  • 2
  • 5
  • 1
    If your app is hosted in a Windows service using UseWindowsService, you get a "System.InvalidOperationException: Stopped without starting" exception from WindowsServiceLifetime. It's thrown in the private Run method based on _delayStart, though I can't work out how to avoid this. https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Hosting.WindowsServices/src/WindowsServiceLifetime.cs – tjmoore Jun 30 '22 at 12:08
  • 1
    You are right, as I found out recently. We temporarily setup the host Windows service to automatically restart after a crash, but clearly it is not a real solution. – Jck Jul 01 '22 at 13:10