千家信息网

.Net 6中WebApplicationBuilder原理和用法是什么

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,这篇文章将为大家详细讲解有关.Net 6中WebApplicationBuilder原理和用法是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。介绍
千家信息网最后更新 2025年01月20日.Net 6中WebApplicationBuilder原理和用法是什么

这篇文章将为大家详细讲解有关.Net 6中WebApplicationBuilder原理和用法是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

介绍

.Net 6为我们带来的一种全新的引导程序启动的方式。与之前的拆分成Program.cs和Startup不同,整个引导启动代码都在Program.cs中。

我们示例程序的第一步是执行WebApplicationBuilder builder = WebApplication.CreateBuilder(args);创建一个WebApplicationBuilder实例。

从命令行中分配Args参数,并将选项对象传递给WebApplicationBuilder构造函数的WebApplicationOptions

  ///         /// Initializes a new instance of the  class with preconfigured defaults.        ///         /// Command line arguments        /// The .        public static WebApplicationBuilder CreateBuilder(string[] args) =>            new(new() { Args = args });

WebApplicationOptions和WebApplicationBuilder后面在讲

internal WebApplicationBuilder(WebApplicationOptions options, Action? configureDefaults = null)    ///     /// Options for configuing the behavior for .    ///     public class WebApplicationOptions    {        ///         /// The command line arguments.        ///         public string[]? Args { get; init; }        ///         /// The environment name.        ///         public string? EnvironmentName { get; init; }        ///         /// The application name.        ///         public string? ApplicationName { get; init; }        ///         /// The content root path.        ///         public string? ContentRootPath { get; init; }        ///         /// The web root path.        ///         public string? WebRootPath { get; init; }    }

WebApplicationBuilder由一堆只读属性和一个方法组成Build(),该方法创建了一个WebApplication. 我删除了部分讲解用不到的代码。

如果您熟悉 ASP.NET Core,那么其中许多属性都使用以前版本中的常见类型

  • IWebHostEnvironment: 用于检索环境

  • IServiceCollection: 用于向 DI 容器注册服务。

  • ConfigurationManager: 用于添加新配置和检索配置值。我在之前的文章有讲

  • ILoggingBuilder: 用于注册额外的日志提供程序

在WebHost和Host性质很有趣,因为它们暴露出新的类型,ConfigureWebHostBuilder和ConfigureHostBuilder。这些类型分别实现IWebHostBuilder和IHostBuilder。

公开IWebHostBuilder和IHostBuilder接口对于允许从.NET 6 之前的应用程序迁移到新的最小托管,我们如何将的lambda风格配置IHostBuilder与命令式风格的WebApplicationBuilder协调起来,这就是ConfigureHostBuilder和ConfigureWebHostBuilder与一些内部沿来IHostBuilder实现。

public sealed class WebApplicationBuilder    {        private const string EndpointRouteBuilderKey = "__EndpointRouteBuilder";        private readonly HostBuilder _hostBuilder = new();        private readonly BootstrapHostBuilder _bootstrapHostBuilder;        private readonly WebApplicationServiceCollection _services = new();        private readonly List> _hostConfigurationValues;        private WebApplication? _builtApplication;        ///         /// Provides information about the web hosting environment an application is running.        ///         public IWebHostEnvironment Environment { get; }        ///         /// A collection of services for the application to compose. This is useful for adding user provided or framework provided services.        ///         public IServiceCollection Services { get; }        ///         /// A collection of configuration providers for the application to compose. This is useful for adding new configuration sources and providers.        ///         public ConfigurationManager Configuration { get; }        ///         /// A collection of logging providers for the application to compose. This is useful for adding new logging providers.        ///         public ILoggingBuilder Logging { get; }        ///         /// An  for configuring server specific properties, but not building.        /// To build after configuration, call .        ///         public ConfigureWebHostBuilder WebHost { get; }        ///         /// An  for configuring host specific properties, but not building.        /// To build after configuration, call .        ///         public ConfigureHostBuilder Host { get; }        ///         /// Builds the .        ///         /// A configured .        public WebApplication Build()        {            // Wire up the host configuration here. We don't try to preserve the configuration            // source itself here since we don't support mutating the host values after creating the builder.            _hostBuilder.ConfigureHostConfiguration(builder =>            {                builder.AddInMemoryCollection(_hostConfigurationValues);            });            var chainedConfigSource = new TrackingChainedConfigurationSource(Configuration);            // Wire up the application configuration by copying the already built configuration providers over to final configuration builder.            // We wrap the existing provider in a configuration source to avoid re-bulding the already added configuration sources.            _hostBuilder.ConfigureAppConfiguration(builder =>            {                builder.Add(chainedConfigSource);                foreach (var (key, value) in ((IConfigurationBuilder)Configuration).Properties)                {                    builder.Properties[key] = value;                }            });            // This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService).            // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection            _hostBuilder.ConfigureServices((context, services) =>            {                // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults                // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen                // until now, so we cannot clear these services even though some are redundant because                // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.                foreach (var s in _services)                {                    services.Add(s);                }                // Add the hosted services that were initially added last                // this makes sure any hosted services that are added run after the initial set                // of hosted services. This means hosted services run before the web host starts.                foreach (var s in _services.HostedServices)                {                    services.Add(s);                }                // Clear the hosted services list out                _services.HostedServices.Clear();                // Add any services to the user visible service collection so that they are observable                // just in case users capture the Services property. Orchard does this to get a "blueprint"                // of the service collection                // Drop the reference to the existing collection and set the inner collection                // to the new one. This allows code that has references to the service collection to still function.                _services.InnerCollection = services;                var hostBuilderProviders = ((IConfigurationRoot)context.Configuration).Providers;                if (!hostBuilderProviders.Contains(chainedConfigSource.BuiltProvider))                {                    // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager.                    // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources.                    ((IConfigurationBuilder)Configuration).Sources.Clear();                }                // Make builder.Configuration match the final configuration. To do that, we add the additional                // providers in the inner _hostBuilders's Configuration to the ConfigurationManager.                foreach (var provider in hostBuilderProviders)                {                    if (!ReferenceEquals(provider, chainedConfigSource.BuiltProvider))                    {                        ((IConfigurationBuilder)Configuration).Add(new ConfigurationProviderSource(provider));                    }                }            });            // Run the other callbacks on the final host builder            Host.RunDeferredCallbacks(_hostBuilder);            _builtApplication = new WebApplication(_hostBuilder.Build());            // Mark the service collection as read-only to prevent future modifications            _services.IsReadOnly = true;            // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the            // service provider ensuring both will be properly disposed with the provider.            _ = _builtApplication.Services.GetService>();            return _builtApplication;        }   private void ConfigureApplication(WebHostBuilderContext context, IApplicationBuilder app)        {            Debug.Assert(_builtApplication is not null);            // UseRouting called before WebApplication such as in a StartupFilter            // lets remove the property and reset it at the end so we don't mess with the routes in the filter            if (app.Properties.TryGetValue(EndpointRouteBuilderKey, out var priorRouteBuilder))            {                app.Properties.Remove(EndpointRouteBuilderKey);            }            if (context.HostingEnvironment.IsDevelopment())            {                app.UseDeveloperExceptionPage();            }            // Wrap the entire destination pipeline in UseRouting() and UseEndpoints(), essentially:            // destination.UseRouting()            // destination.Run(source)            // destination.UseEndpoints()            // Set the route builder so that UseRouting will use the WebApplication as the IEndpointRouteBuilder for route matching            app.Properties.Add(WebApplication.GlobalEndpointRouteBuilderKey, _builtApplication);            // Only call UseRouting() if there are endpoints configured and UseRouting() wasn't called on the global route builder already            if (_builtApplication.DataSources.Count > 0)            {                // If this is set, someone called UseRouting() when a global route builder was already set                if (!_builtApplication.Properties.TryGetValue(EndpointRouteBuilderKey, out var localRouteBuilder))                {                    app.UseRouting();                }                else                {                    // UseEndpoints will be looking for the RouteBuilder so make sure it's set                    app.Properties[EndpointRouteBuilderKey] = localRouteBuilder;                }            }            // Wire the source pipeline to run in the destination pipeline            app.Use(next =>            {                _builtApplication.Run(next);                return _builtApplication.BuildRequestDelegate();            });            if (_builtApplication.DataSources.Count > 0)            {                // We don't know if user code called UseEndpoints(), so we will call it just in case, UseEndpoints() will ignore duplicate DataSources                app.UseEndpoints(_ => { });            }            // Copy the properties to the destination app builder            foreach (var item in _builtApplication.Properties)            {                app.Properties[item.Key] = item.Value;            }            // Remove the route builder to clean up the properties, we're done adding routes to the pipeline            app.Properties.Remove(WebApplication.GlobalEndpointRouteBuilderKey);            // reset route builder if it existed, this is needed for StartupFilters            if (priorRouteBuilder is not null)            {                app.Properties[EndpointRouteBuilderKey] = priorRouteBuilder;            }        }        private sealed class LoggingBuilder : ILoggingBuilder        {            public LoggingBuilder(IServiceCollection services)            {                Services = services;            }            public IServiceCollection Services { get; }        }    }

ConfigureHostBuilder

 public sealed class ConfigureHostBuilder : IHostBuilder, ISupportsConfigureWebHost    IHostBuilder ISupportsConfigureWebHost.ConfigureWebHost(Action configure, Action configureOptions)        {            throw new NotSupportedException("ConfigureWebHost() is not supported by WebApplicationBuilder.Host. Use the WebApplication returned by WebApplicationBuilder.Build() instead.");        }

ConfigureHostBuilder实现IHostBuilder和ISupportsConfigureWebHost,但 ISupportsConfigureWebHost 的实现是假的什么意思呢?

这意味着虽然以下代码可以编译,但是会在运行时抛出异常。

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);builder.Host.ConfigureWebHost(webBuilder =>{    webBuilder.UseStartup();});

ConfigureServices(),该方法Action<>使用IServiceCollection从WebApplicationBuilder. 所以以下两个调用在功能上是相同的:

后一种方法显然不值得在正常实践中使用,但仍然可以使用依赖于这种方法的现有代码,

 public IHostBuilder ConfigureAppConfiguration(Action configureDelegate)    {        // Run these immediately so that they are observable by the imperative code        configureDelegate(_context, _configuration);        return this;    }    public IHostBuilder ConfigureServices(Action configureDelegate)    {        // Run these immediately so that they are observable by the imperative code        configureDelegate(_context, _services);        return this;    }
builder.Services.AddSingleton();builder.Host.ConfigureServices((ctx, services) => services.AddSingleton());

并非所有委托ConfigureHostBuilder都立即传递给运行中的方法。例如UseServiceProviderFactory()保存在列表中,稍后在调用WebApplicationBuilder.Build()

  public IHostBuilder UseServiceProviderFactory(IServiceProviderFactory factory) where TContainerBuilder : notnull        {            if (factory is null)            {                throw new ArgumentNullException(nameof(factory));            }            _operations.Add(b => b.UseServiceProviderFactory(factory));            return this;        }

BootstrapHostBuilder

回到ConfigureHostBuilder我们看内部的BootstrapHostBuilder,它记录了IHostBuilder收到的所有调用。例如ConfigureHostConfiguration()和ConfigureServices(),
这与ConfigureHostBuilder立即执行提供的委托相比,BootstrapHostBuilder的保存将委托提供给稍后执行的列表。这类似于泛型的HostBuilder工作方式。但请注意,这BootstrapHostBuilder调用Build()会引发异常

 internal class BootstrapHostBuilder : IHostBuilder    {        private readonly IServiceCollection _services;        private readonly List> _configureHostActions = new();        private readonly List> _configureAppActions = new();        private readonly List> _configureServicesActions = new();        public IHost Build()        {            // HostingHostBuilderExtensions.ConfigureDefaults should never call this.            throw new InvalidOperationException();        }        public IHostBuilder ConfigureHostConfiguration(Action configureDelegate)        {            _configureHostActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));            return this;        }        public IHostBuilder ConfigureServices(Action configureDelegate)        {            // HostingHostBuilderExtensions.ConfigureDefaults calls this via ConfigureLogging            _configureServicesActions.Add(configureDelegate ?? throw new ArgumentNullException(nameof(configureDelegate)));            return this;        }        // .....    }

WebApplicationBuilder构造函数

最后我们来看WebApplicationBuilder构造函数,注释都给大家翻译了

internal WebApplicationBuilder(WebApplicationOptions options, Action? configureDefaults = null)        {            Services = _services;            var args = options.Args;            //尽早运行方法配置通用和web主机默认值,以从appsettings.json填充配置            //要预填充的环境变量(以DOTNET和ASPNETCORE为前缀)和其他可能的默认源            //正确的默认值。            _bootstrapHostBuilder = new BootstrapHostBuilder(Services, _hostBuilder.Properties);               //不要在这里指定参数,因为我们希望稍后应用它们,以便            //可以覆盖ConfigureWebHostDefaults指定的默认值            _bootstrapHostBuilder.ConfigureDefaults(args: null);            // This is for testing purposes            configureDefaults?.Invoke(_bootstrapHostBuilder);            //我们上次在这里指定了命令行,因为我们跳过了对ConfigureDefaults的调用中的命令行。            //args可以包含主机和应用程序设置,因此我们要确保            //我们适当地订购这些配置提供程序,而不复制它们            if (args is { Length: > 0 })            {                _bootstrapHostBuilder.ConfigureAppConfiguration(config =>                {                    config.AddCommandLine(args);                });            }            // ....        }
// 自ConfigureWebHostDefaults覆盖特定于主机的设置(应用程序名称)以来,上次将参数应用于主机配置。            _bootstrapHostBuilder.ConfigureHostConfiguration(config =>            {                if (args is { Length: > 0 })                {                    config.AddCommandLine(args);                }                // Apply the options after the args                options.ApplyHostConfiguration(config);            });

到此你可能都非常疑惑这玩意到底在干嘛。只要能看明白下面代码就好了,调用BootstrapHostBuilder.RunDefaultCallbacks(),
它以正确的顺序运行我们迄今为止积累的所有存储的回调,以构建HostBuilderContext. 该HostBuilderContext则是用来最终设定的剩余性能WebApplicationBuilder。

完成特定于应用程序的配置后,在由我们手动调用Build()创建一个WebApplication实例。

Configuration = new();            // Collect the hosted services separately since we want those to run after the user's hosted services            _services.TrackHostedServices = true;            // This is the application configuration            var (hostContext, hostConfiguration) = _bootstrapHostBuilder.RunDefaultCallbacks(Configuration, _hostBuilder);            // Stop tracking here            _services.TrackHostedServices = false;            // Capture the host configuration values here. We capture the values so that            // changes to the host configuration have no effect on the final application. The            // host configuration is immutable at this point.            _hostConfigurationValues = new(hostConfiguration.AsEnumerable());            // Grab the WebHostBuilderContext from the property bag to use in the ConfigureWebHostBuilder            var webHostContext = (WebHostBuilderContext)hostContext.Properties[typeof(WebHostBuilderContext)];            // Grab the IWebHostEnvironment from the webHostContext. This also matches the instance in the IServiceCollection.            Environment = webHostContext.HostingEnvironment;            Logging = new LoggingBuilder(Services);            Host = new ConfigureHostBuilder(hostContext, Configuration, Services);            WebHost = new ConfigureWebHostBuilder(webHostContext, Configuration, Services);

WebApplicationBuilder.Build()

该Build()方法不是非常复杂,首先是将配置的配置源复制到_hostBuilder的ConfigurationBuilder实现中。调用此方法时,builder它最初为空,因此这将填充由默认构建器扩展方法添加的所有源,以及您随后配置的额外源。

 // source itself here since we don't support mutating the host values after creating the builder.            _hostBuilder.ConfigureHostConfiguration(builder =>            {                builder.AddInMemoryCollection(_hostConfigurationValues);            });             _hostBuilder.ConfigureAppConfiguration(builder =>            {                builder.Add(chainedConfigSource);                foreach (var (key, value) in ((IConfigurationBuilder)Configuration).Properties)                {                    builder.Properties[key] = value;                }            });

接下来,是一样的事情IServiceCollection,将它们从_services实例复制到_hostBuilder的集合中。

// This needs to go here to avoid adding the IHostedService that boots the server twice (the GenericWebHostService).            // Copy the services that were added via WebApplicationBuilder.Services into the final IServiceCollection            _hostBuilder.ConfigureServices((context, services) =>            {                // We've only added services configured by the GenericWebHostBuilder and WebHost.ConfigureWebDefaults                // at this point. HostBuilder news up a new ServiceCollection in HostBuilder.Build() we haven't seen                // until now, so we cannot clear these services even though some are redundant because                // we called ConfigureWebHostDefaults on both the _deferredHostBuilder and _hostBuilder.                foreach (var s in _services)                {                    services.Add(s);                }                // Add the hosted services that were initially added last                // this makes sure any hosted services that are added run after the initial set                // of hosted services. This means hosted services run before the web host starts.                foreach (var s in _services.HostedServices)                {                    services.Add(s);                }                // Clear the hosted services list out                _services.HostedServices.Clear();                // Add any services to the user visible service collection so that they are observable                // just in case users capture the Services property. Orchard does this to get a "blueprint"                // of the service collection                // Drop the reference to the existing collection and set the inner collection                // to the new one. This allows code that has references to the service collection to still function.                _services.InnerCollection = services;                var hostBuilderProviders = ((IConfigurationRoot)context.Configuration).Providers;                if (!hostBuilderProviders.Contains(chainedConfigSource.BuiltProvider))                {                    // Something removed the _hostBuilder's TrackingChainedConfigurationSource pointing back to the ConfigurationManager.                    // This is likely a test using WebApplicationFactory. Replicate the effect by clearing the ConfingurationManager sources.                    ((IConfigurationBuilder)Configuration).Sources.Clear();                }                // Make builder.Configuration match the final configuration. To do that, we add the additional                // providers in the inner _hostBuilders's Configuration to the ConfigurationManager.                foreach (var provider in hostBuilderProviders)                {                    if (!ReferenceEquals(provider, chainedConfigSource.BuiltProvider))                    {                        ((IConfigurationBuilder)Configuration).Add(new ConfigurationProviderSource(provider));                    }                }            });

接下来运行我们在ConfigureHostBuilder属性中收集的任何回调

// Run the other callbacks on the final host builderHost.RunDeferredCallbacks(_hostBuilder);

最后我们调用_hostBuilder.Build()构建Host实例,并将其传递给 的新实例WebApplication。调用_hostBuilder.Build()是调用所有注册回调的地方。

_builtApplication = new WebApplication(_hostBuilder.Build());

最后,为了保持一切一致ConfigurationManager实例被清除,并链接到存储在WebApplication. 此外IServiceCollectiononWebApplicationBuilder被标记为只读,因此在调用后尝试添加服务WebApplicationBuilder将抛出一个InvalidOperationException. 最后WebApplication返回。

 // Mark the service collection as read-only to prevent future modifications            _services.IsReadOnly = true;            // Resolve both the _hostBuilder's Configuration and builder.Configuration to mark both as resolved within the            // service provider ensuring both will be properly disposed with the provider.            _ = _builtApplication.Services.GetService>();            return _builtApplication;

关于.Net 6中WebApplicationBuilder原理和用法是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

配置 方法 程序 实例 应用 代码 运行 主机 命令 应用程序 函数 参数 属性 文章 稍后 类型 委托 原理 接下来 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 数据库原理与技术第五章答案 台州大数据库安全 远程访问服务器在哪 安卓软件开发自学视频下载 信息技术培训学生网络安全教育 模拟器的软件开发组成 大话西游2最老的服务器是哪个 ef 调用数据库方法 安徽云网络技术包括什么 采集淘宝联盟的数据库 网络安全和程序员 数据库 英文文献 有关网络安全的英语ppt 苏州专业软件开发机构 泰州应用软件开发定制怎么收费 llc网络技术 广州英伦网络技术有限公司 学习通数据库技术习题答案 服务器硬盘有哪几种 构筑疫情防护与网络安全研讨材料 美国在中东地区的网络安全干预 批量删除数据库数据 服务器管理器禁止打开文件 数据无法保存到数据库 美团网络技术北京有限公司 公安网络安全七严禁 8兆专线物理服务器多少钱 foxboro数据库 超星汇雅电子图书数据库的优势 移动模块如何发送消息给服务器
0