I migrated a .NET WebApp from originally 2.1 to 3.1 and 6.0.
I would like to switch to the new 'minimal hosting model' from .NET 6 but still want to keep my long and complex Startup class separated.
I am using Serilog with custom configuration as ILogger implementation: I create a LoggerBuilder in a separate class.
My main looks like this:
public static async Task Main(string[] args)
{
// global shared logger, created BEFORE the host build to be able to log starting and ending the service.
Log.Logger = LoggerBuilder.BuildLogger();
try
{
LoggerAudit.LogAuditAsWarning("Starting " + Constants.ServiceName);
using var source = new CancellationTokenSource();
await CreateWebHost(args).RunAsync(source.Token).ConfigureAwait(false);
//await CreateHost(args).RunAsync(source.Token).ConfigureAwait(false);
//await CreateWebApp(args).RunAsync(source.Token).ConfigureAwait(false);
}
catch (Exception ex)
{
LoggerAudit.GlobalLogAudit.Fatal(ex, Constants.ServiceName + " terminated unexpectedly");
}
finally
{
LoggerAudit.LogAuditAsWarning("Closing " + Constants.ServiceName);
Log.CloseAndFlush();
}
}
with 3 implementations of the Host or WebApp, which should be equivalent
private static IWebHost CreateWebHost(string[] args) // .NET Core 2.1 way
{
IWebHostBuilder builder = WebHost.CreateDefaultBuilder(args)
.UseSerilog()
.SuppressStatusMessages(true)
.ConfigureAppConfiguration((hostContext, config) =>
{
config.BuildConfiguration();
})
.UseStartup<Startup>()
.UseUrls("http://*:5000");
return builder.Build();
}
private static IHost CreateHost(string[] args) // .NET Core 3.1 way
{
IHostBuilder builder = Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder
.SuppressStatusMessages(true)
.ConfigureAppConfiguration((hostContext, config) =>
{
config.BuildConfiguration();
})
.UseStartup<Startup>()
.UseUrls("http://*:5000");
});
return builder.Build();
}
private static WebApplication CreateWebApp(string[] args) // .NET 6 way
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();
var startup = new Startup(builder.Configuration, null);
startup.ConfigureServices(builder.Services);
WebApplication app = builder.Build();
IApiVersionDescriptionProvider provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
startup.Configure(app, app.Environment, provider, app.Lifetime);
return app;
}
All 3 ways are still compatible with .NET 6 and suppose to work fine. Now, UseSerilog() method tells me the WebHost way is obsolete, so I should migrate. So it makes sense to migrate to the newest model with WebApplication.
The catch is the constructor signature of my Startup class
public Startup(IConfiguration config, ILogger<Startup> logger)
I used to still have the CreateWebHost version active.
Switching to CreateHost throws an exception in the Startup constructor:
"Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger`1[Siemens.BT.Edge.CloudConnector.Startup]' while attempting to activate 'Siemens.BT.Edge.CloudConnector.Startup'."
WHY ? To avoid the exception, I can use the Startup factory:
.UseStartup(builderContext => new Startup(builderContext.Configuration, null))
Then the question is: how can I use my ILogger there with Dependency Injection? And the same apply when switching to CreateWebApp.
In any other situation, I should use the
var logger = app.Services.GetService<ILogger<Startup>>();
but obviously, I can't call that before calling the Build method and Startup ctor.
So the better question would be: why did my Startup constructor work with WebHost?