.net 6为我们带来的一种全新的引导程序启动的方式。与之前的拆分成program.cs和startup不同,整个引导启动代码都在program.cs中。
webapplicationbuilder builder = webapplication.createbuilder(args);
在上篇文章中,我简要描述了如何使用 webapplication和webapplicationbuilder配置 asp.net core 应用程序。在这篇文章中,我们来深入看下代码.
我们示例程序的第一感人肺腑的话步是执行webapplicationbu满满正能量的句子ilder builder = webapplication.createbuilder(args);
创建一个webapplicationbuilder
实例。
从命令行中分配args参数,并将选项对象传递给webapplicationbuilder构造函数的webapplicationoptions
/// <summary> /// initializes a new instance of the <e cref="webapplicationbuilder"/> class with preconfigured defaults. /// </summary> /// <param name="args">command line arguments</param> /// <returns>the <e cref="webapplicationbuilder"/>.</returns> public static webapplicationbuilder createbuilder(string[] args) => new(new() { args = args });
webapplicationoptions和webapplicationbuilder后面在讲
internal webapplicationbuilder(webapplicationoptions options, action<ihostbuilder>? configuredefaults = null) /// <summary> /// options for configuing the behavior for <e cref="webapplication.createbuilder(webapplicationoptions)"/>. /// </summary> public class webapplicationoptions { /// <summary> /// the command line arguments. /// </summary> public string[]? args { get; init; } /// <summary> /// the environment name. /// </summary> public string? environmentname { get; init; } /// <summary> /// the application name. /// </summary> public string? applicationname { get; init; } /// <summary> /// the content root path. /// </summary> public string? contentrootpath { get; init; } /// <summary> /// the web root path. /// </summary> public string? webrootpath { get; init; } }
webapplicationbuilder由一堆只读属性和一个方法组成build(),该方法创建了一个webapplication. 我删除了部分讲解用不到的代码。
如果您熟悉 asp.net core,那么其中许多属性都使用以前版本中的常见类型
iwebhostenvironment: 用于检索环境irvicecollection: 用于向 di 容器注册服务。configurationmanager: 用于添加新配置和检索配置值。我在之前的文章有讲iloggingbuilder: 用于注册额外的日志提供程序在webhost和host性质很有趣,因为它们暴露出新的类型,configurewebhostbuilder和configurehostbuilder。这些类型分别实现iwebhostbuilder和ihostbuilder。
公开iwebhostbuilder和ihostbuilder接口对于允许从.net 6 之前的应用程序迁移到新的最小托管,我们如何将的lambda风格配置ihostbuilder与命令式风格的webapplicationbuilder协调起来,这就是configurehostbuilder和configurewebhostbuilder与一些内部沿来ihostbuilder实现。
public aled class webapplicationbuilder{private const string endpointroutebuilderkey = "__endpointroutebuilder";private readonly hostbuilder _hostbuilder = new();private readonly bootstraphostbuilder _bootstraphostbuilder;private readonly webapplicationrvicecollection _rvices = new();private readonly list<keyvaluepair<string, string>> _hostconfigurationvalues;private webapplication? _builtapplication;/// <summary>/// provides information about the web hosting environment an application is running./// </summary>public iwebhostenvironment environment { get; }/// <summary>/// a collection of rvices for the application to compo. this is uful for adding ur provided or framework provided rvices./// </summary>public irvicecollection rvices { get; }/// <summary>/// a collection of configuration providers for the application to compo. this is uful for adding new configuration sources and providers./// </summary>public configurationmanager configuration { get; }/// <summary>/// a collection of logging providers for the application to compo. this is uful for adding new logging providers./// </summary>public iloggingbuilder logging { get; }/// <summary>/// an <e cref="iwebhostbuilder"/> for configuring rver specific properties, but not building./// to build after configuration, call <e cref="build"/>./// </summary>public configurewebhostbuilder webhost { get; }/// <summary>/// an <e cref="ihostbuilder"/> for configuring host specific properties, but not building./// to build after configuration, call <e cref="build"/>./// </summary>public configurehostbuilder host { get; }/// <summary>/// builds the <e cref="webapplication"/>./// </summary>/// <returns>a configured <e cref="webapplication"/>.</returns>public webapplication build(){// wire up the host configuration here. we don't try to prerve the configuration// source itlf 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 ihostedrvice that boots the rver twice (the genericwebhostrvice).// copy the rvices that were added via webapplicationbuilder.rvices into the final irvicecollection_hostbuilder.configurervices((context, rvices) =>{// we've only added rvices configured by the genericwebhostbuilder and webhost.configurewebdefaults// at this point. hostbuilder news up a new rvicecollection in hostbuilder.build() we haven't en// until now, so we cannot clear the rvices even though some are redundant becau// we called configurewebhostdefaults on both the _deferredhostbuilder and _hostbuilder.foreach (var s in _rvices){rvices.add(s);}// add the hosted rvices that were initially added last// this makes sure any hosted rvices that are added run after the initial t// of hosted rvices. this means hosted rvices run before the web host starts.foreach (var s in _rvices.hostedrvices){rvices.add(s);}// clear the hosted rvices list out_rvices.hostedrvices.clear();// add any rvices to the ur visible rvice collection so that they are obrvable// just in ca urs capture the rvices property. orchard does this to get a "blueprint"// of the rvice collection// drop the reference to the existing collection and t the inner collection// to the new one. this allows code that has references to the rvice collection to still function._rvices.innercollection = rvices;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 builderhost.rundeferredcallbacks(_hostbuilder);_builtapplication = new webapplication(_hostbuilder.build());// mark the rvice collection as read-only to prevent future modifications_rvices.isreadonly = true;// resolve both the _hostbuilder's configuration and builder.configuration to mark both as resolved within the// rvice provider ensuring both will be properly dispod with the provider._ = _builtapplication.rvices.getrvice<ienumerable<iconfiguration>>();return _builtapplication;}private void configureapplication(webhostbuildercontext context, iapplicationbuilder app){debug.asrt(_builtapplication is not null);// urouting called before webapplication such as in a startupfilter// lets remove the property and ret it at the end so we don't mess with the routes in the filterif (app.properties.trygetvalue(endpointroutebuilderkey, out var priorroutebuilder)){app.properties.remove(endpointroutebuilderkey);}if (context.hostingenvironment.isdevelopment()){app.udeveloperexceptionpage();}// wrap the entire destination pipeline in urouting() and uendpoints(), esntially:// destination.urouting()// destination.run(source)// destination.uendpoints()// t the route builder so that urouting will u the webapplication as the iendpointroutebuilder for route matchingapp.properties.add(webapplication.globalendpointroutebuilderkey, _builtapplication);// only call urouting() if there are endpoints configured and urouting() wasn't called on the global route builder alreadyif (_builtapplication.datasources.count > 0){// if this is t, someone called urouting() when a global route builder was already tif (!_builtapplication.properties.trygetvalue(endpointroutebuilderkey, out var localroutebuilder)){app.urouting();}el{// uendpoints will be looking for the routebuilder so make sure it's tapp.properties[endpointroutebuilderkey] = localroutebuilder;}}// wire the source pipeline to run in the destination pipelineapp.u(next =>{_builtapplication.run(next);return _builtapplication.buildrequestdelegate();});if (_builtapplication.datasources.count > 0){// we d并列式作文on't know if ur code called uendpoints(), so we will call it just in ca, uendpoints() will ignore duplicate datasourcesapp.uendpoints(_ => { });}// copy the properties to the destination app builderforeach (var item in _builtapplication.properties){app.properties[item.key] = item.value;}// remove the rout鲁滨逊漂流记摘抄e builder to clean up the properties, we're done adding routes to the pipelineapp.properties.remove(webapplication.globalendpointroutebuilderkey);// ret route builder if it existed, this is needed for startupfiltersif (priorroutebuilder is not null){app.properties[endpointroutebuilderkey] = priorroutebuilder;}}private aled class loggingbuilder : iloggingbuilder{public loggingbuilder(irvicecollection rvices){rvices = rvices;}public irvicecollection rvices { get; }}}
public aled class configurehostbuilder : ihostbuilder, isupportsconfigurewebhostihostbuilder isupportsconfigurewebhost.configurewebhost(action<iwebhostbuilder> configure, action<webhostbuilderoptions> configureoptions){throw new notsupportedexception("configurewebhost() is not supported by webapplicationbuilder.host. u the webapplication returned by webapplicationbuilder.build() instead.");}
configurehostbuilder实现ihostbuilder和isupportsconfigurewebhost,但 isupportsconfigurewebhost 的实现是假的什么意思呢?
这意味着虽然以下代码可以编译,但是会在运行时抛出异常。
webapplicationbuilder builder = webapplication.createbuilder(args);builder.host.configurewebhost(webbuilder =>{webbuilder.ustartup<startup>();});
configurervices(),该方法action<>使用irvicecollection从webapplicationbuilder. 所以以下两个调用在功能上是相同的:
后一种方法显然不值得在正常实践中使用,但仍然可以使用依赖于这种方法的现有代码,
public ihostbuilder configureappconfiguration(action<hostbuildercontext, iconfigurationbuilder> configuredelegate){// run the immediately so that they are obrvable by the imperative codeconfiguredelegate(_context, _configuration);return this;}public ihostbuilder configurervices(action<hostbuildercontext, irvicecollection> configuredelegate){// run the immediately so that they are obrvable by the imperative codeconfiguredelegate(_context, _rvices);return this;}
builder.rvices.addsingleton<myimplementation>();builder.host.configurervices((ctx, rvices) => rvices.addsingleton<myimplementation>());
并非所有委托configurehostbuilder都立即传递给运行中的方法。例如urviceproviderfactory()保存在列表中,稍后在调用webapplicationbuilder.build()
public ihostbuilder urviceproviderfactory<tcontainerbuilder>(irviceproviderfactory<tcontainerbuilder> factory) where tcontainerbuilder : notnull{if (factory is null){throw new argumentnullexception(nameof(factory));}_operations.add(b => b.urviceproviderfactory(factory));return this;}
回到configurehostbuilder我们看内部的bootstraphostbuilder,它记录了ihostbuilder收到的所有调用。例如configurehostconfiguration()和configurervices(),
这与configurehostbuilder立即执行提供的委托相比,bootstraphostbuilder的保存将委托提供给稍后执行的列表。这类似于泛型的hostbuilder工作方式。但请注意,这bootstraphostbuilder调用build()会引发异常
internal class bootstraphostbuilder : ihostbuilder{private readonly irvicecollection _rvices;private readonly list<action<iconfigurationbuilder>> _configurehostactions = new();private readonly list<action<hostbuildercontext, iconfigurationbuilder>> _configureappactions = new();private readonly list<action<hostbuildercontext, irvicecollection>> _configurervicesactions = new();public ihost build(){// hostinghostbuilderextensions.configuredefaults should never call this.throw new invalidoperationexception();}public ihostbuilder configurehostconfiguration(action<iconfigurationbuilder> configuredelegate){_configurehostactions.add(configuredelegate ?? throw new argumentnullexception(nameof(configuredelegate)));return this;}public ihostbuilder configurervices(action<hostbuildercontext, irvicecollection> configuredelegate){// hostinghostbuilderextensions.configuredefaults calls this via configurelogging_configurervicesactions.add(configuredelegate ?? throw new argumentnullexception(nameof(configuredelegate)));return this;}// .....}
最后我们来看webapplicationbuilder构造函数,注释都给大家翻译了
internal webapplicationbuilder(webapplicationoptions options, action<ihostbuilder>? configuredefaults = null){rvices = _rvices;var args = options.args;//尽早运行方法配置通用和web主机默认值,以从appttings.json填充配置//要预填充的环境变量(以dotnet和aspnetcore为前缀)和其他可能的默认源//正确的默认值。_bootstraphostbuilder = new bootstraphostbuilder(rvices, _hostbuilder.properties);//不要在这里指定参数,因为我们希望稍后应用它们,以便//可以覆盖configurewebhostdefaults指定的默认值_bootstraphostbuilder.configuredefaults(args: null);// this is for testing purposconfiguredefaults?.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 argsoptions.applyhostconfiguration(config);});
到此你可能都非常疑惑这玩意到底在干嘛。只要能看明白下面代码就好了,调用bootstraphostbuilder.rundefaultcallbacks(),
它以正确的顺序运行我们迄今为止积累的所有存储的回调,以构建hostbuildercontext. 该hostbuildercontext则是用来最终设定的剩余性能webapplicationbuilder。
完成特定于应用程序的配置后,在由我们手动调用build()创建一个webapplication实例。
configuration = new();// collect the hosted rvices parately since we want tho to run after the ur's hosted rvices_rvices.trackhostedrvices = true;// this is the application configurationvar (hostcontext, hostconfiguration) = _bootstraphostbuilder.rundefaultcallbacks(configuration, _hostbuilder);// stop tracking here_rvices.trackhostedrvices = fal;// 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.anumerable());// grab the webhostbuildercontext from the property bag to u in the configurewebhostbuildervar webhostcontext = (webhostbuildercontext)hostcontext.properties[typeof(webhostbuildercontext)];// grab the iwebhostenvironment from the webhostcontext. this also matches the instance in the irvicecollection.environment = webhostcontext.hostingenvironment;logging = new loggingbuilder(rvices);host = new configurehostbuilder(hostcontext, configuration, rvices);webhost = new configurewebhostbuilder(webhostcontext, configuration, rvices);
该build()方法不是非常复杂,首先是将配置的配置源复制到_hostbuilder的configurationbuilder实现中。调用此方法时,builder它最初为空,因此这将填充由默认构建器扩展方法添加的所有源,以及您随后配置的额外源。
// source itlf 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;}});
接下来,是一样的事情irvicecollection,将它们从_rvices实例复制到_hostbuilder的集合中。
// this needs to go here to avoid adding the ihostedrvice that boots the rver twice (the genericwebhostrvice).// copy the rvices that were added via webapplicationbuilder.rvices into the final irvicecollection_hostbuilder.configurervices((context, rvices) =>{// we've only added rvices configured by the genericwebhostbuilder and webhost.configurewebdefaults// at this point. hostbuilder news up a new rvicecollection in hostbuilder.build() we haven't en// until now, so we cannot clear the rvices even though some are redundant becau// we called configurewebhostdefaults on both the _deferredhostbuilder and _hostbuilder.foreach (var s in _rvices){rvices.add(s);}// add the hosted rvices that were initially added last// this makes sure any hosted rvices that are added run after the initial t// of hosted rvices. this means hosted rvices run before the web host starts.foreach (var s in _rvices.hostedrvices){rvices.add(s);}// clear the hosted rvices list out_rvices.hostedrvices.clear();// add any rvices to the ur visible rvice collection so that they are obrvable// just in ca urs capture the rvices property. orchard does this to get a "blueprint"// of the rvice collection// drop the reference to the existing collection and t the inner collection// to the new one. this allows code that has references to the rvice collection to still function._rvices.innercollection = rvices;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 (!reference施瓦辛格的故事equals(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. 此外irvicecollectiononwebapplicationbuilder被标记为只读,因此在调用后尝试添加服务webapplicationbuilder将抛出一个invalidoperationexception. 最后webapplication返回。
// mark the rvice collection as read-only to prevent future modifications_rvices.isreadonly = true;// resolve both the _hostbuilder's configuration and builder.configuration to mark both as resolved within the// rvice provider ensuring both will be properly dispod with the provider._ = _builtapplication.rvices.getrvice<ienumerable<iconfiguration>>();return _builtapplication;
差不多就是这样.
到此这篇关于.net 6中webapplicationbuilder介绍和用法的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持www.887551.com。
本文发布于:2023-04-04 05:07:10,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/bccdf11f5b80a9c5da45d66af322b7e7.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:.Net 6中WebApplicationBuilder介绍和用法.doc
本文 PDF 下载地址:.Net 6中WebApplicationBuilder介绍和用法.pdf
留言与评论(共有 0 条评论) |