加强公司门户网站建设,长沙电子商务网站建设,星子网络公司,朋友圈海报用什么网站做的ASP.NET Core 终于将几乎所有的对象创建工作都和依赖注入框架集成了起来。并对大部分的日常工作进行了抽象。使得整个框架扩展更加方便。各个部分的集成也更加容易。今天我们要思考的部分仍然是从一段每一个工程中都大同小异的代码开始的。IWebHostBuilder CreateWebHostBuilde… ASP.NET Core 终于将几乎所有的对象创建工作都和依赖注入框架集成了起来。并对大部分的日常工作进行了抽象。使得整个框架扩展更加方便。各个部分的集成也更加容易。今天我们要思考的部分仍然是从一段每一个工程中都大同小异的代码开始的。IWebHostBuilder CreateWebHostBuilder(string[] args){ return new WebHostBuilder() .UseKestrel(ko ko.AddServerHeader false) .ConfigureAppConfiguration(cb cb.AddCommandLine(args)) .ConfigureLogging(lb {...}) .UseStartupStartup();}0 太长不读ASP.NET Core 的初始化包含了两个步骤第一个步骤是 Hosting 相关服务的初始化过程初始化完毕之后创建了第一个 IServiceProvider 对象第二步是 Application 相关服务的初始化过程。而 Application 的初始化过程可以注入 Hosting 相关的服务。之后通过 IStartup.ConfigureServices 方法创建了第二个 IServiceProvider 对象。初始化过程中创建的两个 IServiceProvider 均会跟随 WebHost 的销毁而销毁。通过 Startup 类型的构造函数注入的实例是由 Hosting 初始化阶段创建的 IServiceProvider 创建的。只能注入 Hosting 初始化阶段添加的类型。且最好不要使用大量消耗资源的类型。可以在 Startup.Configure 方法中添加其他参数这样会使用 Application 的一个 Scope 下的 IServiceProvider 进行注入且在方法调用完毕之后该 Scope 即被销毁。因此该方法内可以创建资源占用量较高的需要 Dispose 的类型实例而不造成泄露。1 WebHost 的构建主要就是向 IServiceCollection 中添加服务之前提到过任何 Framework 只有两件事情第一件事情就是对象怎么创建第二件事情就是如何将这些创建出来的对象塞到 Framework 处理流水线中。因此 ASP.NET Core 也是这样。在应用程序启动的时候我们会在 WebHostBuilder.Build 方法调用之前进行各种各样的操作虽然我们调用的大部分操作都是扩展方法例如上述代码中的 UseXxx和 ConfigureLogging但是归根结底会调用 IWebHostBuilder 的以下方法IWebHostBuilder ConfigureAppConfiguration(ActionWebHostBuilderContext, IConfigurationBuilder configureDelegate);IWebHostBuilder ConfigureServices(ActionIServiceCollection configureServices);IWebHostBuilder ConfigureServices(ActionWebHostBuilderContext, IServiceCollection configureServices);不论调哪一个方法它们做的事情其实都是一件。就是告诉应用程序我到底有哪些对象需要创建如何创建这些对象以及其生存期如何管理。从技术角度上来说就是将需要创建的对象类型添加到 IServiceCollection 中。如果感兴趣的同学可以看看 WebHostBuilder 的实现代码(https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/WebHostBuilder.cs)就更加清晰了。例如以 ConfigureLogging 为例代码请参见这里(https://github.com/aspnet/Extensions/blob/master/src/Logging/Logging/src/LoggingServiceCollectionExtensions.cs)public static IWebHostBuilder ConfigureLogging( this IWebHostBuilder hostBuilder, ActionWebHostBuilderContext, ILoggingBuilder configureLogging){ return hostBuilder.ConfigureServices((context, collection) collection.AddLogging(builder configureLogging(context, builder)));}public static IServiceCollection AddLogging( this IServiceCollection services, ActionILoggingBuilder configure){ if (services null) { throw new ArgumentNullException(nameof(services)); } services.AddOptions(); services.TryAdd(ServiceDescriptor.SingletonILoggerFactory, LoggerFactory()); services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger), typeof(Logger))); services.TryAddEnumerable(ServiceDescriptor.SingletonIConfigureOptionsLoggerFilterOptions( new DefaultLoggerLevelConfigureOptions(LogLevel.Information))); configure(new LoggingBuilder(services)); return services;}可以看到实际上就是将 IOptions、IOptionsSnapshot、IOptionsMonitor、IOptionsFactory、IOptionsMonitorCache 以及 ILoggerFactory、ILogger、IConfigureOptionsLoggerFilterOptions 添加到 IServiceCollection 中的过程。有关日志的内容我们会在另一篇文章中介绍。2 Startup 初始化时为什么又能注入又有 IServiceCollection 呢在 WebHost 的构建过程中十有八九会出现 UseStartup 这句话如果不出现这句话那么很大程度上使用了 Configure 扩展方法。Startup 是整个 Web 应用程序的起点。应用程序Web App托管在宿主Hosting Environment中。那么它应当是在初始化的最终阶段执行的。我们来观察一下它的典型结构public class Startup{ public void ConfigureServices(IServiceCollection services) { // Add application related services to service collection. } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // Create application pipeline. We will not focus on this method. }}如果单纯观察上述代码那么并没有任何的稀奇之处。ConfigureServices 方法将应用需要的类型全部添加到 IServiceCollection 实例中而 Configure 来构建 Pipeline我们此次不讨论该方法。但是如果我们需要记录日志读取配置文件在应用程序生命周期事件中注册新的处理方法时我们可以将其直接注入 Startup 中。例如public class Startup{ readonly IConfiguration configuration; readonly IApplicationLifetime lifetime; readonly ILoggerStartup logger; public Startup( IConfiguration configuration, IApplicationLifetime lifetime, ILoggerStartup logger) { this.configuration configuration; this.lifetime lifetime; this.logger logger; } public void ConfigureServices(IServiceCollection services) { // Add application related services to service collection. } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // Create application pipeline. }}那么问题就来了。在 Startup 中注入的 configuration、lifetime、logger 这些服务是由哪一个 IServiceProvider 创建出来的呢如果在 Startup 创建时 IServiceProvider 已然创建那么 Startup.ConfigureServices 在向哪个 IServiceCollection 实例添加类型呢应用程序运行期间的 IServiceProvider 是在 Startup 创建之前就创建好的那个呢、还是由 Startup 配置的 IServiceCollection 实例创建的那个呢3 两阶段 ServiceProvider 创建既然 Startup 中已经有一个 IServiceProvider 来给相应的类型进行依赖注入而平时的应用程序中的依赖注入又能够包含 Startup.ConfigureServices 中的类型定义那么说明在整个初始化过程中先后创建了两个 IServiceProvider 对象。即 ASP.NET Core 的初始化包含了两个步骤第一个步骤是 Hosting 相关服务的初始化过程初始化完毕之后创建了第一个 IServiceProvider 对象第二步是 Application 相关服务的初始化过程。而 Application 的初始化过程可以注入 Hosting 相关的服务。之后通过 IStartup.ConfigureServices 方法创建了第二个 IServiceProvider 对象。如果你对源代码感兴趣请参考 WebHostBuilder 类的 Build 方法源代码在这里https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/WebHostBuilder.cs。大致的过程如下BuildCommonServices 方法将所有 Hosting 所需的服务WebHost 相关类型以及所有 IWebHostBuilder 调用中添加的服务类型添加到 IServiceCollection 对象中。使用该 IServiceCollection 创建 Hosting 相关的 IServiceProvider不妨称之为 hostingServiceProvider。使用该 hostingServiceProvider 创建 IStartup 对象这里有和环境相关的 Convension详情请参见上一篇。使用一个复制的 IServiceCollection 对象调用 IStartup.ConfigureServices 方法创建另外一个 IServiceProvider 不妨称之为 applicationServiceProvider。在了解了上述过程之后那么我们需要注意些什么呢首先我们已经了解Startup 可以使用 Hosting 的 IServiceProvider 进行注入。但是 IServiceProvider 是一个顶级的 Provider如果我们在 Startup 中创建了一个非常消耗资源的对象实现了 IDisposable则在默认情况下该对象只有在应用程序彻底退出的时候才会销毁。若显式 Dispose 该对象的话且该对象不是 Transient Scope。则有可能导致 Defect。4 规避初始化过程中的资源泄露但是如果我真的需要在初始化的时候注入非常消耗资源的对象而我又希望规避资源的泄露我该怎么办呢其实还是有办法的。那就是不使用 Startup 的构造函数进行注入而是直接在 Configure 方法中通过参数进行注入。为什么这种方式可以规避资源泄露呢因为这种注入机智并非典型的依赖注入机制而是 ASP.NET Core 特意实现的。如果应用程序在初始化时使用的 UseStartupTStartup() 中的 TStartup 并没有实现 IStartup 的话ASP.NET Core 就会使用基于约定的 IStartup 实现对 TStartup 进行包装。在包装过程中它会尝试找到 TStartup 类型中的 Configure 方法检查参数表中的参数并使用 IStartup.ConfigureServices 创建的 IServiceProvider 进行注入。但是这里的 IServiceProvider 却并不初始化过程中的顶级 Provider。而是在将整个方法调用包裹在了 Scope 里。因此即使在初始化过程中创建非常消耗资源的实例也会随着方法调用结束后 Scope 的 Dispose 而销毁。具体代码请参见ConfigureBuilder 源代码 (https://github.com/aspnet/AspNetCore/blob/master/src/Hosting/Hosting/src/Internal/ConfigureBuilder.cs)5 总结请飞到文章开头的第 0 节 :-D。如果您觉得本文对您有帮助也欢迎分享给其他的人。我们一起进步。欢迎关注我的博客https://clrdaily.com和微信公众号