Skip to content

Commit

Permalink
Add Host.CreateEmptyApplicationBuilder
Browse files Browse the repository at this point in the history
Introduce a way to create an "empty" HostApplicationBuilder that doesn't bring any optional/unnecessary dependencies into the app's closure. This has the same functional behavior as DisableDefaults=true while allowing for the unused code to be trimmed.

Also add CreateApplicationBuilder(HostApplicationBuilderSettings settings) overload to make the Host factory methods complete.

Fix dotnet#81280
  • Loading branch information
eerhardt committed Feb 6, 2023
1 parent 0012f2d commit 9d911d8
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ public ConsoleLifetimeOptions() { }
public static partial class Host
{
public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateApplicationBuilder() { throw null; }
public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateApplicationBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings? settings) { throw null; }
public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateApplicationBuilder(string[]? args) { throw null; }
public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder() { throw null; }
public static Microsoft.Extensions.Hosting.IHostBuilder CreateDefaultBuilder(string[]? args) { throw null; }
public static Microsoft.Extensions.Hosting.HostApplicationBuilder CreateEmptyApplicationBuilder(Microsoft.Extensions.Hosting.HostApplicationBuilderSettings? settings) { throw null; }
}
public sealed partial class HostApplicationBuilder
{
Expand Down
17 changes: 15 additions & 2 deletions src/libraries/Microsoft.Extensions.Hosting/src/Host.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,14 @@ public static IHostBuilder CreateDefaultBuilder(string[]? args)
/// <list type="bullet">
/// <item><description>set the <see cref="IHostEnvironment.ContentRootPath"/> to the result of <see cref="Directory.GetCurrentDirectory()"/></description></item>
/// <item><description>load host <see cref="IConfiguration"/> from "DOTNET_" prefixed environment variables</description></item>
/// <item><description>load host <see cref="IConfiguration"/> from supplied command line args</description></item>
/// <item><description>load app <see cref="IConfiguration"/> from 'appsettings.json' and 'appsettings.[<see cref="IHostEnvironment.EnvironmentName"/>].json'</description></item>
/// <item><description>load app <see cref="IConfiguration"/> from User Secrets when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development' using the entry assembly</description></item>
/// <item><description>load app <see cref="IConfiguration"/> from environment variables</description></item>
/// <item><description>load app <see cref="IConfiguration"/> from supplied command line args</description></item>
/// <item><description>configure the <see cref="ILoggerFactory"/> to log to the console, debug, and event source output</description></item>
/// <item><description>enables scope validation on the dependency injection container when <see cref="IHostEnvironment.EnvironmentName"/> is 'Development'</description></item>
/// </list>
/// </remarks>
/// <returns>The initialized <see cref="HostApplicationBuilder"/>.</returns>
public static HostApplicationBuilder CreateApplicationBuilder() => new HostApplicationBuilder();

/// <summary>
Expand All @@ -93,6 +92,20 @@ public static IHostBuilder CreateDefaultBuilder(string[]? args)
/// </list>
/// </remarks>
/// <param name="args">The command line args.</param>
/// <returns>The initialized <see cref="HostApplicationBuilder"/>.</returns>
public static HostApplicationBuilder CreateApplicationBuilder(string[]? args) => new HostApplicationBuilder(args);

/// <inheritdoc cref="CreateApplicationBuilder()" />
/// <param name="settings">Controls the initial configuration and other settings for constructing the <see cref="HostApplicationBuilder"/>.</param>
public static HostApplicationBuilder CreateApplicationBuilder(HostApplicationBuilderSettings? settings)
=> new HostApplicationBuilder(settings);

/// <summary>
/// Initializes a new instance of the <see cref="HostApplicationBuilder"/> class with no pre-configured defaults.
/// </summary>
/// <param name="settings">Controls the initial configuration and other settings for constructing the <see cref="HostApplicationBuilder"/>.</param>
/// <returns>The initialized <see cref="HostApplicationBuilder"/>.</returns>
public static HostApplicationBuilder CreateEmptyApplicationBuilder(HostApplicationBuilderSettings? settings)
=> new HostApplicationBuilder(settings, empty: true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public sealed class HostApplicationBuilder
{
private readonly HostBuilderContext _hostBuilderContext;
private readonly ServiceCollection _serviceCollection = new();
private readonly IHostEnvironment _environment;
private readonly LoggingBuilder _logging;

private Func<IServiceProvider> _createServiceProvider;
private Action<object> _configureContainer = _ => { };
Expand Down Expand Up @@ -98,6 +100,47 @@ public HostApplicationBuilder(HostApplicationBuilderSettings? settings)
HostingHostBuilderExtensions.AddCommandLineConfig(Configuration, settings.Args);
}

Initialize(settings, out _hostBuilderContext, out _environment, out _logging);

ServiceProviderOptions? serviceProviderOptions = null;
if (!settings.DisableDefaults)
{
HostingHostBuilderExtensions.ApplyDefaultAppConfiguration(_hostBuilderContext, Configuration, settings.Args);
HostingHostBuilderExtensions.AddDefaultServices(_hostBuilderContext, Services);
serviceProviderOptions = HostingHostBuilderExtensions.CreateDefaultServiceProviderOptions(_hostBuilderContext);
}

_createServiceProvider = () =>
{
// Call _configureContainer in case anyone adds callbacks via HostBuilderAdapter.ConfigureContainer<IServiceCollection>() during build.
// Otherwise, this no-ops.
_configureContainer(Services);
return serviceProviderOptions is null ? Services.BuildServiceProvider() : Services.BuildServiceProvider(serviceProviderOptions);
};
}

internal HostApplicationBuilder(HostApplicationBuilderSettings? settings, bool empty)
{
Debug.Assert(empty, "should only be called with empty: true");

settings ??= new HostApplicationBuilderSettings();
Configuration = settings.Configuration ?? new ConfigurationManager();

HostingHostBuilderExtensions.AddCommandLineConfig(Configuration, settings.Args);

Initialize(settings, out _hostBuilderContext, out _environment, out _logging);

_createServiceProvider = () =>
{
// Call _configureContainer in case anyone adds callbacks via HostBuilderAdapter.ConfigureContainer<IServiceCollection>() during build.
// Otherwise, this no-ops.
_configureContainer(Services);
return Services.BuildServiceProvider();
};
}

private void Initialize(HostApplicationBuilderSettings settings, out HostBuilderContext hostBuilderContext, out IHostEnvironment environment, out LoggingBuilder logging)
{
// HostApplicationBuilderSettings override all other config sources.
List<KeyValuePair<string, string?>>? optionList = null;
if (settings.ApplicationName is not null)
Expand All @@ -124,46 +167,29 @@ public HostApplicationBuilder(HostApplicationBuilderSettings? settings)

Configuration.SetFileProvider(physicalFileProvider);

_hostBuilderContext = new HostBuilderContext(new Dictionary<object, object>())
hostBuilderContext = new HostBuilderContext(new Dictionary<object, object>())
{
HostingEnvironment = hostingEnvironment,
Configuration = Configuration,
};

Environment = hostingEnvironment;
environment = hostingEnvironment;

HostBuilder.PopulateServiceCollection(
Services,
_hostBuilderContext,
hostBuilderContext,
hostingEnvironment,
physicalFileProvider,
Configuration,
() => _appServices!);

Logging = new LoggingBuilder(Services);

ServiceProviderOptions? serviceProviderOptions = null;

if (!settings.DisableDefaults)
{
HostingHostBuilderExtensions.ApplyDefaultAppConfiguration(_hostBuilderContext, Configuration, settings.Args);
HostingHostBuilderExtensions.AddDefaultServices(_hostBuilderContext, Services);
serviceProviderOptions = HostingHostBuilderExtensions.CreateDefaultServiceProviderOptions(_hostBuilderContext);
}

_createServiceProvider = () =>
{
// Call _configureContainer in case anyone adds callbacks via HostBuilderAdapter.ConfigureContainer<IServiceCollection>() during build.
// Otherwise, this no-ops.
_configureContainer(Services);
return serviceProviderOptions is null ? Services.BuildServiceProvider() : Services.BuildServiceProvider(serviceProviderOptions);
};
logging = new LoggingBuilder(Services);
}

/// <summary>
/// Provides information about the hosting environment an application is running in.
/// </summary>
public IHostEnvironment Environment { get; }
public IHostEnvironment Environment => _environment;

/// <summary>
/// A collection of services for the application to compose. This is useful for adding user provided or framework provided services.
Expand All @@ -178,7 +204,7 @@ public HostApplicationBuilder(HostApplicationBuilderSettings? settings)
/// <summary>
/// A collection of logging providers for the application to compose. This is useful for adding new logging providers.
/// </summary>
public ILoggingBuilder Logging { get; }
public ILoggingBuilder Logging => _logging;

/// <summary>
/// Registers a <see cref="IServiceProviderFactory{TContainerBuilder}" /> instance to be used to create the <see cref="IServiceProvider" />.
Expand Down
Loading

0 comments on commit 9d911d8

Please sign in to comment.