Reputation: 635
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
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
Upvotes: 30