Skip to content

Commit 313ee06

Browse files
authored
Consume HostApplicationBuilder from WebApplicationBuilder (#40459)
1 parent 40556b2 commit 313ee06

17 files changed

+307
-622
lines changed
Lines changed: 53 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,49 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics;
45
using Microsoft.Extensions.Configuration;
56
using Microsoft.Extensions.DependencyInjection;
6-
using Microsoft.Extensions.FileProviders;
77
using Microsoft.Extensions.Hosting;
88

99
namespace Microsoft.AspNetCore.Hosting;
1010

1111
// This exists solely to bootstrap the configuration
1212
internal sealed class BootstrapHostBuilder : IHostBuilder
1313
{
14-
private readonly IServiceCollection _services;
14+
private readonly HostApplicationBuilder _builder;
15+
1516
private readonly List<Action<IConfigurationBuilder>> _configureHostActions = new();
1617
private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppActions = new();
1718
private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServicesActions = new();
1819

19-
private readonly List<Action<IHostBuilder>> _remainingOperations = new();
20-
21-
public BootstrapHostBuilder(IServiceCollection services, IDictionary<object, object> properties)
20+
public BootstrapHostBuilder(HostApplicationBuilder builder)
2221
{
23-
_services = services;
22+
_builder = builder;
2423

25-
Properties = properties;
24+
foreach (var descriptor in _builder.Services)
25+
{
26+
if (descriptor.ServiceType == typeof(HostBuilderContext))
27+
{
28+
Context = (HostBuilderContext)descriptor.ImplementationInstance!;
29+
break;
30+
}
31+
}
32+
33+
if (Context is null)
34+
{
35+
throw new InvalidOperationException($"{nameof(HostBuilderContext)} must exist in the {nameof(IServiceCollection)}");
36+
}
2637
}
2738

28-
public IDictionary<object, object> Properties { get; }
39+
public IDictionary<object, object> Properties => Context.Properties;
2940

30-
public IHost Build()
41+
public HostBuilderContext Context { get; }
42+
43+
public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
3144
{
32-
// HostingHostBuilderExtensions.ConfigureDefaults should never call this.
33-
throw new InvalidOperationException();
45+
_configureHostActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
46+
return this;
3447
}
3548

3649
public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
@@ -39,123 +52,70 @@ public IHostBuilder ConfigureAppConfiguration(Action<HostBuilderContext, IConfig
3952
return this;
4053
}
4154

42-
public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
55+
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
4356
{
44-
// This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that could change in the future.
45-
// If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder.
46-
if (configureDelegate is null)
47-
{
48-
throw new ArgumentNullException(nameof(configureDelegate));
49-
}
50-
51-
_remainingOperations.Add(hostBuilder => hostBuilder.ConfigureContainer<TContainerBuilder>(configureDelegate));
57+
_configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
5258
return this;
5359
}
5460

55-
public IHostBuilder ConfigureHostConfiguration(Action<IConfigurationBuilder> configureDelegate)
61+
public IHost Build()
5662
{
57-
_configureHostActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
58-
return this;
63+
// ConfigureWebHostDefaults should never call this.
64+
throw new InvalidOperationException();
5965
}
6066

61-
public IHostBuilder ConfigureServices(Action<HostBuilderContext, IServiceCollection> configureDelegate)
67+
public IHostBuilder ConfigureContainer<TContainerBuilder>(Action<HostBuilderContext, TContainerBuilder> configureDelegate)
6268
{
63-
// HostingHostBuilderExtensions.ConfigureDefaults calls this via ConfigureLogging
64-
_configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));
65-
return this;
69+
// ConfigureWebHostDefaults should never call this.
70+
throw new InvalidOperationException();
6671
}
6772

6873
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory) where TContainerBuilder : notnull
6974
{
70-
// This is not called by HostingHostBuilderExtensions.ConfigureDefaults currently, but that could change in the future.
71-
// If this does get called in the future, it should be called again at a later stage on the ConfigureHostBuilder.
72-
if (factory is null)
73-
{
74-
throw new ArgumentNullException(nameof(factory));
75-
}
76-
77-
_remainingOperations.Add(hostBuilder => hostBuilder.UseServiceProviderFactory<TContainerBuilder>(factory));
78-
return this;
75+
// ConfigureWebHostDefaults should never call this.
76+
throw new InvalidOperationException();
7977
}
8078

8179
public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory) where TContainerBuilder : notnull
8280
{
83-
// HostingHostBuilderExtensions.ConfigureDefaults calls this via UseDefaultServiceProvider
84-
// during the initial config stage. It should be called again later on the ConfigureHostBuilder.
85-
if (factory is null)
86-
{
87-
throw new ArgumentNullException(nameof(factory));
88-
}
89-
90-
_remainingOperations.Add(hostBuilder => hostBuilder.UseServiceProviderFactory<TContainerBuilder>(factory));
91-
return this;
81+
// ConfigureWebHostDefaults should never call this.
82+
throw new InvalidOperationException();
9283
}
9384

94-
public (HostBuilderContext, ConfigurationManager) RunDefaultCallbacks(ConfigurationManager configuration, HostBuilder innerBuilder)
85+
public ServiceDescriptor RunDefaultCallbacks()
9586
{
96-
var hostConfiguration = new ConfigurationManager();
97-
9887
foreach (var configureHostAction in _configureHostActions)
9988
{
100-
configureHostAction(hostConfiguration);
89+
configureHostAction(_builder.Configuration);
10190
}
10291

103-
// This is the hosting environment based on configuration we've seen so far.
104-
var hostingEnvironment = new HostingEnvironment()
105-
{
106-
// ApplicationKey is always configured by WebApplicationOptions, so it's never expected to be null
107-
ApplicationName = hostConfiguration[HostDefaults.ApplicationKey]!,
108-
EnvironmentName = hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
109-
ContentRootPath = HostingPathResolver.ResolvePath(hostConfiguration[HostDefaults.ContentRootKey]),
110-
};
111-
112-
hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(hostingEnvironment.ContentRootPath);
113-
114-
// Normalize the content root setting for the path in configuration
115-
hostConfiguration[HostDefaults.ContentRootKey] = hostingEnvironment.ContentRootPath;
116-
117-
var hostContext = new HostBuilderContext(Properties)
118-
{
119-
Configuration = hostConfiguration,
120-
HostingEnvironment = hostingEnvironment,
121-
};
122-
123-
// Split the host configuration and app configuration so that the
124-
// subsequent callback don't get a chance to modify the host configuration.
125-
configuration.SetBasePath(hostingEnvironment.ContentRootPath);
126-
127-
// Chain the host configuration and app configuration together.
128-
configuration.AddConfiguration(hostConfiguration, shouldDisposeConfiguration: true);
129-
13092
// ConfigureAppConfiguration cannot modify the host configuration because doing so could
13193
// change the environment, content root and application name which is not allowed at this stage.
13294
foreach (var configureAppAction in _configureAppActions)
13395
{
134-
configureAppAction(hostContext, configuration);
96+
configureAppAction(Context, _builder.Configuration);
13597
}
13698

137-
// Update the host context, everything from here sees the final
138-
// app configuration
139-
hostContext.Configuration = configuration;
140-
14199
foreach (var configureServicesAction in _configureServicesActions)
142100
{
143-
configureServicesAction(hostContext, _services);
101+
configureServicesAction(Context, _builder.Services);
144102
}
145103

146-
foreach (var callback in _remainingOperations)
104+
ServiceDescriptor? genericWebHostServiceDescriptor = null;
105+
106+
for (int i = _builder.Services.Count - 1; i >= 0; i--)
147107
{
148-
callback(innerBuilder);
108+
var descriptor = _builder.Services[i];
109+
if (descriptor.ServiceType == typeof(IHostedService))
110+
{
111+
Debug.Assert(descriptor.ImplementationType?.Name == "GenericWebHostService");
112+
113+
genericWebHostServiceDescriptor = descriptor;
114+
_builder.Services.RemoveAt(i);
115+
break;
116+
}
149117
}
150118

151-
return (hostContext, hostConfiguration);
152-
}
153-
154-
private class HostingEnvironment : IHostEnvironment
155-
{
156-
public string EnvironmentName { get; set; } = default!;
157-
public string? ApplicationName { get; set; }
158-
public string ContentRootPath { get; set; } = default!;
159-
public IFileProvider ContentRootFileProvider { get; set; } = default!;
119+
return genericWebHostServiceDescriptor ?? throw new InvalidOperationException($"GenericWebHostedService must exist in the {nameof(IServiceCollection)}");
160120
}
161121
}

src/DefaultBuilder/src/ConfigurationProviderSource.cs

Lines changed: 0 additions & 98 deletions
This file was deleted.

0 commit comments

Comments
 (0)