WebHost主机
/// <summary> /// A builder for <see cref="IWebHost"/> /// </summary> public class WebHostBuilder : IWebHostBuilder { private readonly HostingEnvironment _hostingEnvironment; private Action<WebHostBuilderContext, IServiceCollection> _configureServices; private IConfiguration _config; private WebHostOptions _options; private WebHostBuilderContext _context; private bool _webHostBuilt; private Action<WebHostBuilderContext, IConfigurationBuilder> _configureAppConfigurationBuilder; /// <summary> /// Initializes a new instance of the <see cref="WebHostBuilder"/> class. /// </summary> public WebHostBuilder() { _hostingEnvironment = new HostingEnvironment(); _config = new ConfigurationBuilder() .AddEnvironmentVariables(prefix: "ASPNETCORE_") .Build(); if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.EnvironmentKey))) { // Try adding legacy environment keys, never remove these. UseSetting(WebHostDefaults.EnvironmentKey, Environment.GetEnvironmentVariable("Hosting:Environment") ?? Environment.GetEnvironmentVariable("ASPNET_ENV")); } if (string.IsNullOrEmpty(GetSetting(WebHostDefaults.ServerUrlsKey))) { // Try adding legacy url key, never remove this. UseSetting(WebHostDefaults.ServerUrlsKey, Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS")); } _context = new WebHostBuilderContext { Configuration = _config }; } /// <summary> /// Get the setting value from the configuration. /// </summary> /// <param name="key">The key of the setting to look up.</param> /// <returns>The value the setting currently contains.</returns> public string GetSetting(string key) { return _config[key]; } /// <summary> /// Add or replace a setting in the configuration. /// </summary> /// <param name="key">The key of the setting to add or replace.</param> /// <param name="value">The value of the setting to add or replace.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder UseSetting(string key, string value) { _config[key] = value; return this; } /// <summary> /// Adds a delegate for configuring additional services for the host or web application. This may be called /// multiple times. /// </summary> /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices) { if (configureServices == null) { throw new ArgumentNullException(nameof(configureServices)); } return ConfigureServices((_, services) => configureServices(services)); } /// <summary> /// Adds a delegate for configuring additional services for the host or web application. This may be called /// multiple times. /// </summary> /// <param name="configureServices">A delegate for configuring the <see cref="IServiceCollection"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices) { _configureServices += configureServices; return this; } /// <summary> /// Adds a delegate for configuring the <see cref="IConfigurationBuilder"/> that will construct an <see cref="IConfiguration"/>. /// </summary> /// <param name="configureDelegate">The delegate for configuring the <see cref="IConfigurationBuilder" /> that will be used to construct an <see cref="IConfiguration" />.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> /// <remarks> /// The <see cref="IConfiguration"/> and <see cref="ILoggerFactory"/> on the <see cref="WebHostBuilderContext"/> are uninitialized at this stage. /// The <see cref="IConfigurationBuilder"/> is pre-populated with the settings of the <see cref="IWebHostBuilder"/>. /// </remarks> public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) { _configureAppConfigurationBuilder += configureDelegate; return this; } /// <summary> /// Builds the required services and an <see cref="IWebHost"/> which hosts a web application. /// </summary> public IWebHost Build() { if (_webHostBuilt) { throw new InvalidOperationException(Resources.WebHostBuilder_SingleInstance); } _webHostBuilt = true; var hostingServices = BuildCommonServices(out var hostingStartupErrors); var applicationServices = hostingServices.Clone(); var hostingServiceProvider = GetProviderFromFactory(hostingServices); if (!_options.SuppressStatusMessages) { // Warn about deprecated environment variables if (Environment.GetEnvironmentVariable("Hosting:Environment") != null) { Console.WriteLine("The environment variable 'Hosting:Environment' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'"); } if (Environment.GetEnvironmentVariable("ASPNET_ENV") != null) { Console.WriteLine("The environment variable 'ASPNET_ENV' is obsolete and has been replaced with 'ASPNETCORE_ENVIRONMENT'"); } if (Environment.GetEnvironmentVariable("ASPNETCORE_SERVER.URLS") != null) { Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'"); } } AddApplicationServices(applicationServices, hostingServiceProvider); var host = new WebHost( applicationServices, hostingServiceProvider, _options, _config, hostingStartupErrors); try { host.Initialize(); // resolve configuration explicitly once to mark it as resolved within the // service provider, ensuring it will be properly disposed with the provider _ = host.Services.GetService<IConfiguration>(); var logger = host.Services.GetRequiredService<ILogger<WebHost>>(); // Warn about duplicate HostingStartupAssemblies foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().GroupBy(a => a, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1)) { logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once."); } return host; } catch { // Dispose the host if there's a failure to initialize, this should dispose // services that were constructed until the exception was thrown host.Dispose(); throw; } IServiceProvider GetProviderFromFactory(IServiceCollection collection) { var provider = collection.BuildServiceProvider(); var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>(); if (factory != null && !(factory is DefaultServiceProviderFactory)) { using (provider) { return factory.CreateServiceProvider(factory.CreateBuilder(collection)); } } return provider; } } private IServiceCollection BuildCommonServices(out AggregateException hostingStartupErrors) { hostingStartupErrors = null; _options = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name); if (!_options.PreventHostingStartup) { var exceptions = new List<Exception>(); // Execute the hosting startup assemblies foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase)) { try { var assembly = Assembly.Load(new AssemblyName(assemblyName)); foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>()) { var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType); hostingStartup.Configure(this); } } catch (Exception ex) { // Capture any errors that happen during startup exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex)); } } if (exceptions.Count > 0) { hostingStartupErrors = new AggregateException(exceptions); } } var contentRootPath = ResolveContentRootPath(_options.ContentRootPath, AppContext.BaseDirectory); // Initialize the hosting environment ((IWebHostEnvironment)_hostingEnvironment).Initialize(contentRootPath, _options); _context.HostingEnvironment = _hostingEnvironment; var services = new ServiceCollection(); services.AddSingleton(_options); services.AddSingleton<IWebHostEnvironment>(_hostingEnvironment); services.AddSingleton<IHostEnvironment>(_hostingEnvironment); #pragma warning disable CS0618 // Type or member is obsolete services.AddSingleton<AspNetCore.Hosting.IHostingEnvironment>(_hostingEnvironment); services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment); #pragma warning restore CS0618 // Type or member is obsolete services.AddSingleton(_context); var builder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_config, shouldDisposeConfiguration: true); _configureAppConfigurationBuilder?.Invoke(_context, builder); var configuration = builder.Build(); // register configuration as factory to make it dispose with the service provider services.AddSingleton<IConfiguration>(_ => configuration); _context.Configuration = configuration; var listener = new DiagnosticListener("Microsoft.AspNetCore"); services.AddSingleton<DiagnosticListener>(listener); services.AddSingleton<DiagnosticSource>(listener); services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>(); services.AddTransient<IHttpContextFactory, DefaultHttpContextFactory>(); services.AddScoped<IMiddlewareFactory, MiddlewareFactory>(); services.AddOptions(); services.AddLogging(); services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>(); if (!string.IsNullOrEmpty(_options.StartupAssembly)) { try { var startupType = StartupLoader.FindStartupType(_options.StartupAssembly, _hostingEnvironment.EnvironmentName); if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo())) { services.AddSingleton(typeof(IStartup), startupType); } else { services.AddSingleton(typeof(IStartup), sp => { var hostingEnvironment = sp.GetRequiredService<IHostEnvironment>(); var methods = StartupLoader.LoadMethods(sp, startupType, hostingEnvironment.EnvironmentName); return new ConventionBasedStartup(methods); }); } } catch (Exception ex) { var capture = ExceptionDispatchInfo.Capture(ex); services.AddSingleton<IStartup>(_ => { capture.Throw(); return null; }); } } _configureServices?.Invoke(_context, services); return services; } private void AddApplicationServices(IServiceCollection services, IServiceProvider hostingServiceProvider) { // We are forwarding services from hosting container so hosting container // can still manage their lifetime (disposal) shared instances with application services. // NOTE: This code overrides original services lifetime. Instances would always be singleton in // application container. var listener = hostingServiceProvider.GetService<DiagnosticListener>(); services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticListener), listener)); services.Replace(ServiceDescriptor.Singleton(typeof(DiagnosticSource), listener)); } private string ResolveContentRootPath(string contentRootPath, string basePath) { if (string.IsNullOrEmpty(contentRootPath)) { return basePath; } if (Path.IsPathRooted(contentRootPath)) { return contentRootPath; } return Path.Combine(Path.GetFullPath(basePath), contentRootPath); } }
看下WebHostBuilder是如何构建WebHost的
核心方法:BuildCommonServices
从程序集中找到实现有HostingStartupAttribute的特性,创建IHostingStartup对象,配置当前的WebHostBuilder
初始化IWebHostEnvironment,指定FileProvider
hostingEnvironment.ContentRootFileProvider = new PhysicalFileProvider(hostingEnvironment.ContentRootPath); var webRoot = options.WebRoot; if (webRoot == null) { // Default to /wwwroot if it exists. var wwwroot = Path.Combine(hostingEnvironment.ContentRootPath, "wwwroot"); if (Directory.Exists(wwwroot)) { hostingEnvironment.WebRootPath = wwwroot; } } else { hostingEnvironment.WebRootPath = Path.Combine(hostingEnvironment.ContentRootPath, webRoot); } if (!string.IsNullOrEmpty(hostingEnvironment.WebRootPath)) { hostingEnvironment.WebRootPath = Path.GetFullPath(hostingEnvironment.WebRootPath); if (!Directory.Exists(hostingEnvironment.WebRootPath)) { Directory.CreateDirectory(hostingEnvironment.WebRootPath); } hostingEnvironment.WebRootFileProvider = new PhysicalFileProvider(hostingEnvironment.WebRootPath); } else { hostingEnvironment.WebRootFileProvider = new NullFileProvider(); }
接下来创建应用的配置和注入指定的服务
var services = new ServiceCollection(); services.AddSingleton(_options); services.AddSingleton<IWebHostEnvironment>(_hostingEnvironment); services.AddSingleton<IHostEnvironment>(_hostingEnvironment); #pragma warning disable CS0618 // Type or member is obsolete services.AddSingleton<AspNetCore.Hosting.IHostingEnvironment>(_hostingEnvironment); services.AddSingleton<Extensions.Hosting.IHostingEnvironment>(_hostingEnvironment); #pragma warning restore CS0618 // Type or member is obsolete services.AddSingleton(_context); var builder = new ConfigurationBuilder() .SetBasePath(_hostingEnvironment.ContentRootPath) .AddConfiguration(_config, shouldDisposeConfiguration: true); _configureAppConfigurationBuilder?.Invoke(_context, builder); var configuration = builder.Build(); // register configuration as factory to make it dispose with the service provider services.AddSingleton<IConfiguration>(_ => configuration); _context.Configuration = configuration; var listener = new DiagnosticListener("Microsoft.AspNetCore"); services.AddSingleton<DiagnosticListener>(listener); services.AddSingleton<DiagnosticSource>(listener); services.AddTransient<IApplicationBuilderFactory, ApplicationBuilderFactory>(); services.AddTransient<IHttpContextFactory, DefaultHttpContextFactory>(); services.AddScoped<IMiddlewareFactory, MiddlewareFactory>(); services.AddOptions(); services.AddLogging(); services.AddTransient<IServiceProviderFactory<IServiceCollection>, DefaultServiceProviderFactory>();
几个重要的服务:IApplicationBuilderFactory、IHttpContextFactory、IMiddlewareFactory、IServiceProviderFactory<IServiceCollection>
如果_options.StartupAssembly指定了值,则从该程序集中获取名字为Startup的类型,如果该类型实现IStartup,则直接注册,
否则找到该类型下面的Configure{0}、Configure{0}Container、Configure{0}Services方法,其中{0}是环境名称占位符
internal class StartupLoader { // Creates an <see cref="StartupMethods"/> instance with the actions to run for configuring the application services and the // request pipeline of the application. // When using convention based startup, the process for initializing the services is as follows: // The host looks for a method with the signature <see cref="IServiceProvider"/> ConfigureServices(<see cref="IServiceCollection"/> services). // If it can't find one, it looks for a method with the signature <see cref="void"/> ConfigureServices(<see cref="IServiceCollection"/> services). // When the configure services method is void returning, the host builds a services configuration function that runs all the <see cref="IStartupConfigureServicesFilter"/> // instances registered on the host, along with the ConfigureServices method following a decorator pattern. // Additionally to the ConfigureServices method, the Startup class can define a <see cref="void"/> ConfigureContainer<TContainerBuilder>(TContainerBuilder builder) // method that further configures services into the container. If the ConfigureContainer method is defined, the services configuration function // creates a TContainerBuilder <see cref="IServiceProviderFactory{TContainerBuilder}"/> and runs all the <see cref="IStartupConfigureContainerFilter{TContainerBuilder}"/> // instances registered on the host, along with the ConfigureContainer method following a decorator pattern. // For example: // StartupFilter1 // StartupFilter2 // ConfigureServices // StartupFilter2 // StartupFilter1 // ConfigureContainerFilter1 // ConfigureContainerFilter2 // ConfigureContainer // ConfigureContainerFilter2 // ConfigureContainerFilter1 // // If the Startup class ConfigureServices returns an <see cref="IServiceProvider"/> and there is at least an <see cref="IStartupConfigureServicesFilter"/> registered we // throw as the filters can't be applied. public static StartupMethods LoadMethods(IServiceProvider hostingServiceProvider, Type startupType, string environmentName) { var configureMethod = FindConfigureDelegate(startupType, environmentName); var servicesMethod = FindConfigureServicesDelegate(startupType, environmentName); var configureContainerMethod = FindConfigureContainerDelegate(startupType, environmentName); object instance = null; if (!configureMethod.MethodInfo.IsStatic || (servicesMethod != null && !servicesMethod.MethodInfo.IsStatic)) { instance = ActivatorUtilities.GetServiceOrCreateInstance(hostingServiceProvider, startupType); } // The type of the TContainerBuilder. If there is no ConfigureContainer method we can just use object as it's not // going to be used for anything. var type = configureContainerMethod.MethodInfo != null ? configureContainerMethod.GetContainerType() : typeof(object); var builder = (ConfigureServicesDelegateBuilder) Activator.CreateInstance( typeof(ConfigureServicesDelegateBuilder<>).MakeGenericType(type), hostingServiceProvider, servicesMethod, configureContainerMethod, instance); return new StartupMethods(instance, configureMethod.Build(instance), builder.Build()); } private abstract class ConfigureServicesDelegateBuilder { public abstract Func<IServiceCollection, IServiceProvider> Build(); } private class ConfigureServicesDelegateBuilder<TContainerBuilder> : ConfigureServicesDelegateBuilder { public ConfigureServicesDelegateBuilder( IServiceProvider hostingServiceProvider, ConfigureServicesBuilder configureServicesBuilder, ConfigureContainerBuilder configureContainerBuilder, object instance) { HostingServiceProvider = hostingServiceProvider; ConfigureServicesBuilder = configureServicesBuilder; ConfigureContainerBuilder = configureContainerBuilder; Instance = instance; } public IServiceProvider HostingServiceProvider { get; } public ConfigureServicesBuilder ConfigureServicesBuilder { get; } public ConfigureContainerBuilder ConfigureContainerBuilder { get; } public object Instance { get; } public override Func<IServiceCollection, IServiceProvider> Build() { ConfigureServicesBuilder.StartupServiceFilters = BuildStartupServicesFilterPipeline; var configureServicesCallback = ConfigureServicesBuilder.Build(Instance); ConfigureContainerBuilder.ConfigureContainerFilters = ConfigureContainerPipeline; var configureContainerCallback = ConfigureContainerBuilder.Build(Instance); return ConfigureServices(configureServicesCallback, configureContainerCallback); Action<object> ConfigureContainerPipeline(Action<object> action) { return Target; // The ConfigureContainer pipeline needs an Action<TContainerBuilder> as source, so we just adapt the // signature with this function. void Source(TContainerBuilder containerBuilder) => action(containerBuilder); // The ConfigureContainerBuilder.ConfigureContainerFilters expects an Action<object> as value, but our pipeline // produces an Action<TContainerBuilder> given a source, so we wrap it on an Action<object> that internally casts // the object containerBuilder to TContainerBuilder to match the expected signature of our ConfigureContainer pipeline. void Target(object containerBuilder) => BuildStartupConfigureContainerFiltersPipeline(Source)((TContainerBuilder)containerBuilder); } } Func<IServiceCollection, IServiceProvider> ConfigureServices( Func<IServiceCollection, IServiceProvider> configureServicesCallback, Action<object> configureContainerCallback) { return ConfigureServicesWithContainerConfiguration; IServiceProvider ConfigureServicesWithContainerConfiguration(IServiceCollection services) { // Call ConfigureServices, if that returned an IServiceProvider, we're done IServiceProvider applicationServiceProvider = configureServicesCallback.Invoke(services); if (applicationServiceProvider != null) { return applicationServiceProvider; } // If there's a ConfigureContainer method if (ConfigureContainerBuilder.MethodInfo != null) { var serviceProviderFactory = HostingServiceProvider.GetRequiredService<IServiceProviderFactory<TContainerBuilder>>(); var builder = serviceProviderFactory.CreateBuilder(services); configureContainerCallback(builder); applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder); } else { // Get the default factory var serviceProviderFactory = HostingServiceProvider.GetRequiredService<IServiceProviderFactory<IServiceCollection>>(); var builder = serviceProviderFactory.CreateBuilder(services); applicationServiceProvider = serviceProviderFactory.CreateServiceProvider(builder); } return applicationServiceProvider ?? services.BuildServiceProvider(); } } private Func<IServiceCollection, IServiceProvider> BuildStartupServicesFilterPipeline(Func<IServiceCollection, IServiceProvider> startup) { return RunPipeline; IServiceProvider RunPipeline(IServiceCollection services) { #pragma warning disable CS0612 // Type or member is obsolete var filters = HostingServiceProvider.GetRequiredService<IEnumerable<IStartupConfigureServicesFilter>>().Reverse().ToArray(); #pragma warning restore CS0612 // Type or member is obsolete // If there are no filters just run startup (makes IServiceProvider ConfigureServices(IServiceCollection services) work. if (filters.Length == 0) { return startup(services); } Action<IServiceCollection> pipeline = InvokeStartup; for (int i = 0; i < filters.Length; i++) { pipeline = filters[i].ConfigureServices(pipeline); } pipeline(services); // We return null so that the host here builds the container (same result as void ConfigureServices(IServiceCollection services); return null; void InvokeStartup(IServiceCollection serviceCollection) { var result = startup(serviceCollection); if (filters.Length > 0 && result != null) { // public IServiceProvider ConfigureServices(IServiceCollection serviceCollection) is not compatible with IStartupServicesFilter; #pragma warning disable CS0612 // Type or member is obsolete var message = $"A ConfigureServices method that returns an {nameof(IServiceProvider)} is " + $"not compatible with the use of one or more {nameof(IStartupConfigureServicesFilter)}. " + $"Use a void returning ConfigureServices method instead or a ConfigureContainer method."; #pragma warning restore CS0612 // Type or member is obsolete throw new InvalidOperationException(message); }; } } } private Action<TContainerBuilder> BuildStartupConfigureContainerFiltersPipeline(Action<TContainerBuilder> configureContainer) { return RunPipeline; void RunPipeline(TContainerBuilder containerBuilder) { var filters = HostingServiceProvider #pragma warning disable CS0612 // Type or member is obsolete .GetRequiredService<IEnumerable<IStartupConfigureContainerFilter<TContainerBuilder>>>() #pragma warning restore CS0612 // Type or member is obsolete .Reverse() .ToArray(); Action<TContainerBuilder> pipeline = InvokeConfigureContainer; for (int i = 0; i < filters.Length; i++) { pipeline = filters[i].ConfigureContainer(pipeline); } pipeline(containerBuilder); void InvokeConfigureContainer(TContainerBuilder builder) => configureContainer(builder); } } } public static Type FindStartupType(string startupAssemblyName, string environmentName) { if (string.IsNullOrEmpty(startupAssemblyName)) { throw new ArgumentException( string.Format("A startup method, startup type or startup assembly is required. If specifying an assembly, '{0}' cannot be null or empty.", nameof(startupAssemblyName)), nameof(startupAssemblyName)); } var assembly = Assembly.Load(new AssemblyName(startupAssemblyName)); if (assembly == null) { throw new InvalidOperationException(String.Format("The assembly '{0}' failed to load.", startupAssemblyName)); } var startupNameWithEnv = "Startup" + environmentName; var startupNameWithoutEnv = "Startup"; // Check the most likely places first var type = assembly.GetType(startupNameWithEnv) ?? assembly.GetType(startupAssemblyName + "." + startupNameWithEnv) ?? assembly.GetType(startupNameWithoutEnv) ?? assembly.GetType(startupAssemblyName + "." + startupNameWithoutEnv); if (type == null) { // Full scan var definedTypes = assembly.DefinedTypes.ToList(); var startupType1 = definedTypes.Where(info => info.Name.Equals(startupNameWithEnv, StringComparison.OrdinalIgnoreCase)); var startupType2 = definedTypes.Where(info => info.Name.Equals(startupNameWithoutEnv, StringComparison.OrdinalIgnoreCase)); var typeInfo = startupType1.Concat(startupType2).FirstOrDefault(); if (typeInfo != null) { type = typeInfo.AsType(); } } if (type == null) { throw new InvalidOperationException(String.Format("A type named '{0}' or '{1}' could not be found in assembly '{2}'.", startupNameWithEnv, startupNameWithoutEnv, startupAssemblyName)); } return type; } internal static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName) { var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true); return new ConfigureBuilder(configureMethod); } internal static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName) { var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false); return new ConfigureContainerBuilder(configureMethod); } internal static bool HasConfigureServicesIServiceProviderDelegate(Type startupType, string environmentName) { return null != FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false); } internal static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName) { var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false) ?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false); return new ConfigureServicesBuilder(servicesMethod); } private static MethodInfo FindMethod(Type startupType, string methodName, string environmentName, Type returnType = null, bool required = true) { var methodNameWithEnv = string.Format(CultureInfo.InvariantCulture, methodName, environmentName); var methodNameWithNoEnv = string.Format(CultureInfo.InvariantCulture, methodName, ""); var methods = startupType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static); var selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithEnv, StringComparison.OrdinalIgnoreCase)).ToList(); if (selectedMethods.Count > 1) { throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithEnv)); } if (selectedMethods.Count == 0) { selectedMethods = methods.Where(method => method.Name.Equals(methodNameWithNoEnv, StringComparison.OrdinalIgnoreCase)).ToList(); if (selectedMethods.Count > 1) { throw new InvalidOperationException(string.Format("Having multiple overloads of method '{0}' is not supported.", methodNameWithNoEnv)); } } var methodInfo = selectedMethods.FirstOrDefault(); if (methodInfo == null) { if (required) { throw new InvalidOperationException(string.Format("A public method named '{0}' or '{1}' could not be found in the '{2}' type.", methodNameWithEnv, methodNameWithNoEnv, startupType.FullName)); } return null; } if (returnType != null && methodInfo.ReturnType != returnType) { if (required) { throw new InvalidOperationException(string.Format("The '{0}' method in the type '{1}' must have a return type of '{2}'.", methodInfo.Name, startupType.FullName, returnType.Name)); } return null; } return methodInfo; } }
创建IServiceProvider
IServiceProvider GetProviderFromFactory(IServiceCollection collection) { var provider = collection.BuildServiceProvider(); var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>(); if (factory != null && !(factory is DefaultServiceProviderFactory)) { using (provider) { return factory.CreateServiceProvider(factory.CreateBuilder(collection)); } } return provider; }
最后创建WebHost
var host = new WebHost( applicationServices, hostingServiceProvider, _options, _config, hostingStartupErrors); try { host.Initialize(); // resolve configuration explicitly once to mark it as resolved within the // service provider, ensuring it will be properly disposed with the provider _ = host.Services.GetService<IConfiguration>(); var logger = host.Services.GetRequiredService<ILogger<WebHost>>(); // Warn about duplicate HostingStartupAssemblies foreach (var assemblyName in _options.GetFinalHostingStartupAssemblies().GroupBy(a => a, StringComparer.OrdinalIgnoreCase).Where(g => g.Count() > 1)) { logger.LogWarning($"The assembly {assemblyName} was specified multiple times. Hosting startup assemblies should only be specified once."); } return host; } catch { // Dispose the host if there's a failure to initialize, this should dispose // services that were constructed until the exception was thrown host.Dispose(); throw; }