dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.07k stars 9.9k forks source link

Add a way to customize WebApplicationBuilder before Build is called #44599

Open kunga opened 1 year ago

kunga commented 1 year ago

Background and Motivation

Before the Minimal API there was IStartupFilter interface that allowed us to customize existing IApplicationBuilder. But I can't find something similar for WebApplicationBuilder.

It would be really useful to have such an option. For example, WebApplicationBuilder.Logging immediately executes provided code, so it is impossible to use configuration sources or other services that can be added later.

As I found, Serilog developers faced the same issue here: instead of simple calling WebApplicationBuilder.Logging.AddProvider they have to do lazy initialization via ConfigureServices.AddSingleton(SerilogFactory) and moreover implement their own ILoggerFactory for that.

Same thoughts may be also applied for WebApplication.

Proposed API

Add startup filters for WebApplicationBuilder and WebApplication:

public interface IWebApplicationBuilderStartupFilter
{
    Action<WebApplicationBuilder> Configure(Action<WebApplicationBuilder> next);
}

public interface IWebApplicationStartupFilter
{
    Action<WebApplication> Configure(Action<WebApplication> next);
}

Usage Examples

public class WebApplicationBuilderStartupFilter : IWebApplicationBuilderStartupFilter
{
    public Action<WebApplicationBuilder> Configure(Action<WebApplicationBuilder> next)
    {
        return builder =>
        {
            var someRequiredColdLogSettings = builder.Configuration.Get<MyLoggerSettings>();

            builder.Logging.AddProvider(new MyLoggerProvider(someRequiredColdLogSettings));

            next(builder);
        };
    }
}

Alternative Designs

Instead of some startup filters there may be some pre build/run actions.

Risks

I'm not an expert of AspNetCore, so if there is another way to solve my issue please tell me.

davidfowl commented 1 year ago

IStartupFilter should still work, albeit slightly differently.

It would be really useful to have such an option. For example, WebApplicationBuilder.Logging immediately executes provided code, so it is impossible to use configuration sources or other services that can be added later.

You can use IHostingStartup for this, it still works (though it is tied to the IWebHostBuilder APIs).

kunga commented 1 year ago

IStartupFilter should still work, albeit slightly differently.

It would be really useful to have such an option. For example, WebApplicationBuilder.Logging immediately executes provided code, so it is impossible to use configuration sources or other services that can be added later.

You can use IHostingStartup for this, it still works (though it is tied to the IWebHostBuilder APIs).

How can I add IHostingStartup to WebApplicationBuilder?

But anyway, I want to use whole WebApplicationBuilder, so that it's the recommended way to configure host for now.

davidfowl commented 1 year ago

How can I add IHostingStartup to WebApplicationBuilder?

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/platform-specific-configuration?view=aspnetcore-6.0

I'm not too keen on adding something like this mostly because of it doesn't work on the other host. IHostingStartup are currently work on both old and new hosts.

What sort of API are you looking for and what is your exact scenario?

kunga commented 1 year ago

I'm a developer of core libraries such as tracing, logging and metrics instrumentation.

I want to create my Log, Tracer, MetricsContext components and register them in the DI container. For that I want to use WebApplicationBuilder.Logging.AddProvider and WebApplicationBuilder.Host.ConfigureServises.

But I can’t create them immediately when some WebApplicationBuilder.AddMyComponents method is called because they may require additional settings that user may provide later:

WebApplicationBuilder.AddMyComponents(); //some initial setup, creates Log and calls WebApplicationBuilder.Logging.AddProvider
builder.Services.Configure<MyLogOptions>(...); //some additional log setup, that should be applied to created Log

I can hack it and provide components factories to ConfigureServises, however it requires a lot of complex code like these or these.

Instead, I can implement some IWebApplicationBuilderStartupFilter that would be called right before WebApplicationBuilder.Build method and create and register my components there.

kunga commented 1 year ago

https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/platform-specific-configuration?view=aspnetcore-6.0

As I see, IHostingStartup is usable only with the attribute, so I can't integrate it with my external Log/Tracing/Metrics libraries.

It is usable only in main assembly.

kunga commented 1 year ago

I'm not too keen on adding something like this mostly because of it doesn't work on the other host. IHostingStartup are currently work on both old and new hosts.

And don't you plan to remove old builders at all in next .NET versions?)

davidfowl commented 1 year ago

And don't you plan to remove old builders at all in next .NET versions?)

No.

peteraritchie commented 1 year ago

I went down this rabbit hole a bit and tried to implement IHostingStartup. What I found was that the Nuget package that originally housed IHostingStartup (Microsoft.AspNetCore.Hosting.Abstractions) is deprecated. IHostingStartup is documented as now being housed in Microsoft.AspNetCore.App.Ref but that package is described as "an internal implementation of the .NET Core SDK and is not meant to be used as a normal PackageReference.".

Use hosting startup assemblies in ASP.NET Core describes creating a class library or a console app to implement IHostingStartup. But, when trying to add the nuget package included in the IHostingStartup documentation (and ignoring Use hosting startup assemblies in ASP.NET Core telling you to use "Microsoft.AspNetCore.Hosting.Abstractions"), the following error is the result:

NU1213: The package Microsoft.AspNetCore.App.Ref 7.0.5 has a package type DotnetPlatform that is incompatible with this project. 
Package 'Microsoft.AspNetCore.App.Ref 7.0.5' has a package type 'DotnetPlatform' that is not supported by project

Question @davidfowl, is IHostingStartup and IStartupFilter still supported in ASP.Net Core 7 and 8?