Tovich
Tovich

Reputation: 635

C# - .NET 6 - Console app with Generic Host vs without

I am using the new top-level statements in .NET 6 to create a simple console application, but I don't understand the advantages/disadvantages of using the "Generic Host". Can you explain?

My code with Generic Host:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

Console.WriteLine("Hello, World!");

using var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((_, services) =>
    {
        services.AddTransient<ITestInterface, TestClass>();
    })
    .Build();

Test();
Console.ReadKey();

void Test()
{
    var testClass = host.Services.GetRequiredService<ITestInterface>();
    testClass.TestMethod();
}

versus

using Microsoft.Extensions.DependencyInjection;

Console.WriteLine("Hello, World!");

var services = new ServiceCollection();
services.AddTransient<ITestInterface, TestClass>();
var servicesProvider = services.BuildServiceProvider();

Test();
Console.ReadKey();

void Test()
{
    var testClass = servicesProvider.GetRequiredService<ITestInterface>();
    testClass.TestMethod();
}

Upvotes: 30

Views: 22292

Answers (1)

Peter Bons
Peter Bons

Reputation: 29870

The benefits of using the generic host is that by default a lot of services are already setup for you, see the docs.

The CreateDefaultBuilder method:

  • Sets the content root to the path returned by GetCurrentDirectory().
  • Loads host configuration from:
    • Environment variables prefixed with DOTNET_.
    • Command-line arguments.
  • Loads app configuration from:
    • appsettings.json.
    • appsettings.{Environment}.json.
    • Secret Manager when the app runs in the Development environment.
    • Environment variables.
    • Command-line arguments.
  • Adds the following logging providers:
    • Console
    • Debug
    • EventSource
    • EventLog (only when running on Windows)
  • Enables scope validation and dependency validation when the environment is Development.

The ConfigureServices method exposes the ability to add services to the Microsoft.Extensions.DependencyInjection.IServiceCollection instance. Later, these services can be made available from dependency injection.

You are not using the generic host correctly. For instance: normally one would add a hosted service so you can use proper DI instead of resolving the required services manually.

An example can be found at the docs

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<Worker>();
            });
}

If we extend this example with an implementation of Worker that takes in a dependency it will look like this:

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddTransient<ITestInterface, TestClass>();
                services.AddHostedService<Worker>();
            });
}

internal class Worker : IHostedService
{
    public Worker(ITestInterface testClass)
    {
        testClass.Foo();
    }


    public Task StartAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}

public interface ITestInterface
{
    void Foo();
}

public class TestClass : ITestInterface
{
    public void Foo()
    {

    }
}

Now you see a new instance of Worker is created and an instance of ITestInterface is injected. There is no need to call servicesProvider.GetRequiredService<ITestInterface>(); which is an anti-pattern.

Decision Tree

  • If you don't need all those additional services you can choose not to use the Generic Host like in your second code example in the question.
  • If you do want to make use of services like logging, app configuration etc. you should use the Generic Host.

Upvotes: 30

Related Questions