Repository navigation
[API Proposal]: Add IHostApplicationBuilder interface #85486
Description
Background and motivation
In .NET 6, ASP.NET added a WebApplicationBuilder "minimal hosting" API. This was a new, non-callback approach to the HostBuilder Generic Host API. In .NET 7, Extensions.Hosting added a similar "generic host" API that followed the same non-callback API design.
The problem is these two hosting APIs have the same members (mostly), but don't have a common base abstraction that libraries can add extension methods to. In the previous, callback API IHostBuilder was the interface libraries could extend and add their library-specific DI services, etc. to.
To enable this, we should add a general abstraction to these hosting builder APIs.
See the conversations at:
- Update the Worker template to use
Host.CreateApplicationBuilderaspnetcore#47720 (comment) - tweak that simplifies Worker startup code aspnetcore#43056
API Proposal
namespace Microsoft.Extensions.Hosting;
public interface IHostApplicationBuilder
{
IDictionary<object, object> Properties { get; }
IConfigurationManager Configuration { get; }
IHostEnvironment Environment { get; }
ILoggingBuilder Logging { get; }
IServiceCollection Services { get; }
IHost Build(); // See Note below on `Build()`
void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder>factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder: notnull;
}
-public sealed class HostApplicationBuilder {}
+public sealed class HostApplicationBuilder : IHostApplicationBuilder {}
namespace Microsoft.Extensions.Configuration;
public interface IConfigurationManager : IConfiguration, IConfigurationBuilder
{
}
-public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable {}
+public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable, IConfigurationManager {}Note on Build()
If we wanted to make this API work for all ApplicationBuilders like MauiAppBuilder and WebAssemblyHostBuilder, we would need to think about IHost Build(). Those 2 app models follow this same ApplicationBuilder pattern, but the things they build don't implement IHost. Options here are:
- Don't put
Build()in the interface. But the name of the interface is "Builder", so it doesn't feel right to not have aBuildmethod. - For the app models that don't support
IHost, they would throw NotSupported for this method. This is my recommendation.
API Usage
Using an existing extension method on IHostBuilder as an example, a new extension method could be written like:
public static class HostBuilderExtensions
{
public static IHostBuilder AddSteeltoe(this IHostApplicationBuilder hostBuilder, IEnumerable<string> exclusions = null, ILoggerFactory loggerFactory = null)
{
...
hostBuilder.Properties[LoggerName] = logger;
hostBuilder.Configuration.AddSteeltoeConfigServer();
hostBuilder.Services.AddMongoClient()
}
}Alternative Designs
We could make WebApplicationBuilder and HostApplicationBuilder explicitly implement IHostBuilder, but this breaks the API design of making the hosting code minimal, and running inline. It would cause a lot of confusion on when services, config, etc gets added in which order. For this reason, we don't want to keep using the IHostBuilder interface in new code.
Risks
N/A
Activity
eerhardt commentedon Apr 27, 2023
cc @davidfowl @halter73
davidfowl commentedon Apr 27, 2023
We have to fix the Configuration API to allow reading it. Maybe we need an interface for the ConfigurationManager (this is why interfaces are great 😄).
eerhardt commentedon Apr 27, 2023
How do you feel about 2 properties?
?
davidfowl commentedon Apr 27, 2023
Doesn't feel good, but it's an option.
eerhardt commentedon Apr 28, 2023
Updated the proposed API to include a new
interface IConfigurationManager : IConfiguration, IConfigurationBuildereerhardt commentedon Apr 28, 2023
cc @dotnet/area-extensions-configuration as well, since this proposal spans both Hosting and Configuration.
steveharter commentedon May 3, 2023
Who is planning on driving and implementing this?
ericstj commentedon May 4, 2023
@eerhardt @halter73 -- does this proposal imply libraries need to extend both
IHostBuilderandIHostApplicationBuilderif they'd want to work in both minimal APIs and not?halter73 commentedon May 4, 2023
The majority of libraries can get by just extending
IServiceCollection. But if libraries need to also add config source and add services in a single method, or read the config/environment before adding services, they'll need to extend bothIHostBuilderandIHostApplicationBuilderas you say.I was able to update
UseWindowsServiceandUseSystemdwhich previously targetedIHostBuilderto targetIServiceCollectioninstead by reading theIHostEnvironmentwhen constructing services rather than when adding them collection. I was hoping that mostIHostBuilderextension methods could be updated to do something similar to avoid adding yet another builder interface.If you extend just
IHostBuilder, it will often still work withWebApplicationBuildervia theWebApplicationBuilder.Hostproperty unless the extension method tries changing the host environment (e.g. by changing the content root) or callingBuild()which is simply unsupported.We could consider adding a similar
IHostBuilderproperty toHostApplicationBuilder. We've already done most of the work to implement it because that's what's exposed via the "Microsoft.Extensions.Hosting"DiagnosticListener, but we decided against this in the originalHostApplicationBuilderAPI review because of the inability to change certain host configuration keys likeHostDefaults.ContentRootKeyinConfigureHostConfigurationor callBuild()was clunky.runtime/src/libraries/Microsoft.Extensions.Hosting/src/HostBuilder.cs
Lines 181 to 191 in 87189ec
runtime/src/libraries/Microsoft.Extensions.Hosting/src/HostApplicationBuilder.cs
Lines 307 to 314 in 87189ec
eerhardt commentedon May 4, 2023
Agreed. That is what I found most libraries doing. Only a few, bigger libraries actually needed to extend
IHostBuilder.https://grep.app/search?q=this%20IHostBuilder
There are quite a few of cases I found that are just using
.ConfigureServices.I am not in favor of doing this. I think we need to break away from the old, callback approach. My reasoning is in the
Alternative Designssection above.davidfowl commentedon May 4, 2023
Let's push forward with @eerhardt 's proposal.
14 remaining items
tarekgh commentedon May 19, 2023
@eerhardt
@noahfalk is currently working on creating the proposal. So, it is not out yet.
davidfowl commentedon May 19, 2023
Part of that was that we know of new things that will be on these builders. Not saying it will be an abstract base class (you know how much I like those!) but we do need to evolve this API.
eerhardt commentedon May 19, 2023
We've established using DIMs in Microsoft.Extensions.* for
net6.0+ targets. For example, see #50406.davidfowl commentedon May 19, 2023
I love it!
noahfalk commentedon May 19, 2023
I have my working draft here: https://gist.github.com/noahfalk/32bb919907c972ac06ac128bc767d48d#integration-with-hosting-apis
I've never worked in the hosting APIs before so the current design follows the philosophy "duplicate whatever ILoggingBuilder did". Feedback is welcome if you think there is something we should be doing differently.
terrajobst commentedon May 30, 2023
Video
Buildmethodnamespace Microsoft.Extensions.Hosting; public interface IHostApplicationBuilder { IDictionary<object, object> Properties { get; } IConfigurationManager Configuration { get; } IHostEnvironment Environment { get; } ILoggingBuilder Logging { get; } IServiceCollection Services { get; } void ConfigureContainer<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder>factory, Action<TContainerBuilder>? configure = null) where TContainerBuilder: notnull; } -public sealed class HostApplicationBuilder {} +public sealed class HostApplicationBuilder : IHostApplicationBuilder {}namespace Microsoft.Extensions.Configuration; public interface IConfigurationManager : IConfiguration, IConfigurationBuilder { } -public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable {} +public sealed class ConfigurationManager : IConfigurationBuilder, IConfigurationRoot, IDisposable, IConfigurationManager {}Add IHostApplicationBuilder interface (#86974)