Hosting Extensions – Delegates

Hosting Extensions – Delegates

Here is a hosting extension that I use often. It is an extension method that allows me to write a super quick console application like this:

class Program
{
    static void Main(string[] args)
    {
        Host.CreateDefaultBuilder()
            .Build()
            .RunDelegate(host =>
            {
                // Put my logic here.
            });
    }
}

Most of this is nothing new, of course, except for that RunDelegate method. That little method allows me to call a delegate and pass in the newly constructed host object, as a parameter. I like to use this approach for writing simple tools, or servers, or whenever I don’t anything more complicated than a .NET delegate.

Here is the listing for the method:

public static void RunDelegate(
    this IHost host,
    Action<IHost> action
    )
{
    Guard.Instance().ThrowIfNull(host, nameof(host))
        .ThrowIfNull(action, nameof(action));

    action(host);
}

Not a whole lot to go wrong there. The method validates the incoming host and action parameters. Then, it calls the action, passing in the host object, as a parameter.

I also have an asynchronous version of this same method, called RunDelegateAsync, which looks like this:

public static async Task RunDelegateAsync(
    this IHost host,
    Action<IHost, CancellationToken> action,
    CancellationToken token = default
    )
{
    Guard.Instance().ThrowIfNull(host, nameof(host))
        .ThrowIfNull(action, nameof(action));

    await Task.Run(
        () => action(host, token),
        token
        ).ConfigureAwait(false);
}

Once again, the method validates the incoming parameters. It then creates a task for the delegate and runs it, passing in the cancellation token – just in case we need to stop everything in a hurry.

Now, granted, I could have easily run a delegate, and passed in the host, without writing these methods. But, there are two reasons why I did: (1) it makes the application startup that much more uniform, and (2) if I decide I want to add anything to the application startup, I can add that logic here, in these methods, and have my changes show up in every application I’ve ever used these method in.

I also have a version of this idea that works directly with an IHostBuilder instance. Here is what that method looks like:

public static void RunDelegate(
    this IHostBuilder hostBuilder,
    Action<IHost, CancellationToken> hostDelegate,
    CancellationToken cancellationToken = default
    )
{
    Guard.Instance().ThrowIfNull(hostBuilder, nameof(hostBuilder))
        .ThrowIfNull(hostDelegate, nameof(hostDelegate));

    var host = hostBuilder.UseConsoleLifetime()
        .Build();

    try
    {
        hostDelegate(
            host, 
            cancellationToken
            );
    }
    finally
    {
        host.StopAsync(
            cancellationToken
            ).Wait();
    }
}

This method starts by calling UseConsoleLifetime, which directs the builder to wire up a console for the application. The method will work without this, on Windows, but it will complain on Linux, so I added that here. Afterwards, we call Build on the builder, to create the host instance. Once we do that, we only have to call our delegate, passing in our new IHost instance. Finally, we finish things up by calling StopAsync on the host, in order to clean things up on our way out.

At the time I write this, I’ve yet to write an asynchronous extension of IHostBuilder, for the RunDelegate method. Somehow, I’ve just yet to have a need for it, yet. At some point I’ll probably add that to the NUGET package though.

Everything I covered in the blog post is part of the CG.Hosting NUGET package. The source is available HERE. The package itself can be downloaded HERE.

Thanks for reading!

Photo by Christiann Koepke on Unsplash