长春企业做网站,苏州城乡和住房建设局网站首页,广告设计公司企业文化,新开传奇ASP.NET Core的核心是通过一个Server和若干注册的Middleware构成的管道#xff0c;不论是管道自身的构建#xff0c;还是Server和Middleware自身的实现#xff0c;以及构建在这个管道的应用#xff0c;都需要相应的服务提供支持#xff0c;ASP.NET Core自身提供了一个DI容… ASP.NET Core的核心是通过一个Server和若干注册的Middleware构成的管道不论是管道自身的构建还是Server和Middleware自身的实现以及构建在这个管道的应用都需要相应的服务提供支持ASP.NET Core自身提供了一个DI容器来实现针对服务的注册和消费。换句话说不只是ASP.NET Core底层框架使用的服务是由这个DI容器来注册和提供应用级别的服务的注册和提供也需要以来这个DI容器所以正如本文标题所说的——学习ASP.NET Core你必须了解无处不在的“依赖注入”。 目录一、依赖注入简介 二、依赖注入在管道构建过程中的应用 三、依赖服务的注册与注入 四、让Startup的ConfigureServices方法返回一个ServiceProvider 五、ASP.NET Core默认注册了哪些服务 六、ASP.NET Core MVC中的依赖注入 一、依赖注入简介 说到依赖注入Dependency Injection以下简称DI就必须说IoCInverse of Control很多人将这两这混为一谈其实这是两个完全不同的概念或者是不同“层次”的两个概念我曾在《控制反转IoC》和《依赖注入DI》对这两个概念做过详细介绍。ASP.NET Core使用的DI框架由“Micorosoft.Extensions.DependencyInjection”这个NuGet包来承载我们也可以非ASP.NET Core应用或者你自己的框架上单独使用它对于这个DI框架的设计、实现以及编程相关的内容我在系列文章《ASP.NET Core 中的依赖注入 [共7篇]》对此有过详细的介绍。 DI框架具有两个核心的功能即服务的注册和提供这两个功能分别由对应的对象来承载, 它们分别是ServiceCollection和ServiceProvider。如下图所示我们将相应的服务以不同的生命周期模式Transient、Scoped和Singleton注册到ServiceCollection对象之上在利用后者创建的ServiceProvider根据注册的服务类型提取相应的服务对象。 二、依赖注入在管道构建过程中的使用 在ASP.NET Core管道的构架过程中主要涉及三个对象/类型作为宿主的WebHost和他的创建者WebHostBuilder以及注册到WebHostBuilder的Startup类型。 如下的代码片段体现了启动ASP.NET Core应用采用的典型编程模式我们首先创建一个WebHostBuilder对象并将采用Server和Startup类型注册到它之上。在调用Build方法创建WebHost之前我们还可以调用相应的方式做其他所需的注册工作。当我们调用WebHost的Run方法之后后者会利用注册的Startup类型来构建完整的管道。那么在管道的构建过程中DI是如何被应用的呢 1: new WebHostBuilder() 2: .UseKestrel() 3: .UseStartupStartup() 4: .Xxx 5: .Build() 6: .Run() DI在管道ASP.NET Core管道构建过程中的应用基本体现下面这个序列图中。当我们调用WebHostBuilder的Build方法创建对应的WebHost的时候前者会创建一个ServiceCollection对象并将一系列预定义的服务注册在它之上。接下来WebHostBuilder会利用这个ServiceCollection对象创建出对应的ServieProvider这个ServiceProvider和ServiceCollection对象会一并传递给最终创建WebHost对象。当我们调用WebHost的Run方法启动它的时候如果注册的Startup是一个实例类型它会利用这个ServiceProvider以构造器注入的方式创建对应的Startup对象。说的具体一点我们注册的Startup类型的构造函数是允许定义参数的但是参数类型必须是预先注册到ServiceCollection中的服务类型。 注册的Startup方法可以包含一个可选的ConfigureServices方法这个方法具有一个类型为IServiceCollection接口的参数。WebHost会将WebHostBuilder传递给它的ServiceCollection作为参数调用这个ConfigureServices方法而我们则利用这个方法将注册的中间件和应用所需的服务注册到这个ServiceCollection对象上。在这之后所有需要的服务包括框架和应用注册的服务都注册到这个ServiceCollection上面WebHost会利用它创建一个新的ServiceProvider。WebHost会利用这个ServiceProvider对象以方法注入的方式调用Startup对象/类型的Configure方法最终完成你对整个管道的建立。换句话会说定义在Startup类型中旨在用于注册Middleware的Configure方法除了采用IApplicationBuilder作为第一个参数之外它依然可以采用注册的任何一个服务类型作为后续参数的类型。 服务的注册除了是现在注册的Startup类型的ConfigureServices方法之外实际上还具有另一个实现方式那就是调用WebHostBuilder具有如下定义的ConfigureServices方法。当WebHostBuilder创建出ServiceCollection对象并完成了默认服务的注册后我们通过调用这个方法所传入的所有ActionIServiceCollection对象将最终应用到这个ServiceCollection对象上。 1: public interface IWebHostBuilder 2: { 3: IWebHostBuilder ConfigureServiecs(ActionIServiceCollection configureServices); 4: } 值得一提的是Startup类型的ConfigureServices方法是允许具有一个IServiceProvider类型的返回值如果这个方法返回一个具体的ServiceProrivder那么WebHost将不会利用ServiceCollection来创建ServiceProvider而是直接使用这个返回的ServiceProvider来调用Startup对象/类型的Configure方法。这实际上是一个很有用的扩展点我们使用它可以实现针对其它DI框架的集成。 三、依赖服务的注册与注入 接下来我们通过一个实例来演示如何利用Startup类型的ConfigureServices来注册服务以及发生在Startup类型上的两种依赖注入形式。如下面的代码片段所示我们定义了两个服务接口IFoo和IBar和对应的实现类型Foo和Bar。其中其中服务Foo是通过调用WebHostBuilder的ConfigureServices方法进行注册的而另一个服务Bar的注册则发生在Startup的ConfigureServices方法上。对于Startup来说它具有一个类型为IFoo的只读属性该属性在构造函数利用传入的参数进行初始化不用是这体现了针对Startup的构造器注入。Startup的Configure方法除了ApplicationBuilder作为第一个参数之外还具有另一个类型为IBar的参数我们利用它来演示方法注入。 1: public interface IFoo { } 2: public interface IBar { } 3: public class Foo : IFoo { } 4: public class Bar : IBar { } 5: 6: public class Program 7: { 8: public static void Main(string[] args) 9: { 10: new WebHostBuilder() 11: .ConfigureServices(servicesservices.AddSingletonIFoo, Foo()) 12: .UseKestrel() 13: .UseStartupStartup() 14: .Build() 15: .Run(); 16: } 17: } 18: public class Startup 19: { 20: public IFoo Foo { get; private set; } 21: public Startup(IFoo foo) 22: { 23: this.Foo foo; 24: } 25: public void ConfigureServices(IServiceCollection services) 26: { 27: services.AddTransientIBar, Bar(); 28: } 29: 30: public void Configure(IApplicationBuilder app, IBar bar) 31: { 32: app.Run(async context 33: { 34: context.Response.ContentType text/html; 35: await context.Response.WriteAsync($IFoo{this.Foo}br/); 36: await context.Response.WriteAsync($IBar{bar}); 37: }); 38: } 39: } 在Startup的Configure方法中我们调用ApplicationBulder的Run方法注册了一个Middleware后者将两个注入的服务的类型作为响应的内容。当我们运行这个应用并利用浏览器访问默认的监听地址http://localhost:5000时浏览器会将注入的两个服务对象的类型以下图的方式展现出来。 四、让Startup的ConfigureServices方法返回一个ServiceProvider 我们说注册的Startup类型的ConfigureServices允许返回一个ServiceProvider这个特性的重要意义在于它使我们可以实现与第三方DI框架比如Unity、Castle、Ninject和AutoFac等的集成。我们照例采用一个实例对此做一个演示简单起见我们并不会真正利用某个具体的DI框架来创建这个ServiceProvider而是直接创建一个新的ServiceCollection来创建它,为此我们对上面这个程序进行了如下的改写。 1: public class Program 2: { 3: public static void Main(string[] args) 4: { 5: new WebHostBuilder() 6: .UseKestrel() 7: .UseStartupStartup() 8: .Build() 9: .Run(); 10: } 11: } 12: public class Startup 13: { 14: public IServiceProvider ConfigureServices(IServiceCollection services) 15: { 16: IServiceCollection newServices new ServiceCollection(); 17: foreach (ServiceDescriptor service in services) 18: { 19: newServices.Add(service); 20: } 21: 22: return newServices 23: .AddSingletonIFoo, Foo() 24: .AddSingletonIBar, Bar() 25: .BuildServiceProvider(); 26: } 27: 28: public void Configure(IApplicationBuilder app, IFoo foo, IBar bar) 29: { 30: app.Run(async context 31: { 32: context.Response.ContentType text/html; 33: await context.Response.WriteAsync($IFoo{foo}br/); 34: await context.Response.WriteAsync($IBar{bar}); 35: }); 36: } 37: } 如上面的代码片段所示在Startup的ConfigureServices方法中我们通过拷贝注册到现有ServiceCollection的所有ServiceDescriptor生成了一个新的ServiceCollection两个服务Foo和Bar被注册到后者之上。该方法最终返回由这个新ServiceCollection创建的ServiceProvider。在另一个Configure方法中我们添加了两个类型分别为IFoo和IBar的参数并以相同的方式将它们的真实类型名称和注册服务类型的映射关系作为响应内容。程序运行之后我们利用浏览器进行访问照样会得到一样的结果。 五、ASP.NET Core默认注册了哪些服务 WebHostBuilder在创建ServiceCollection之后会注册一些默认的服务。这些服务和我们自行注册的服务并没有任何区别只要我们知道对应的服务类型就可以通过注入的方式获取并使用它们。那么具体由哪些服务被默认注册了呢如下所示的是这些服务对应的类型至于这些服务各自有何用途我们在这里就先不深究了。 IHostingEnvironmentILoggerFactoryILoggerIApplicationBuilderFactoryIHttpContextFactoryIOptionsDiagnosticSourceDiagnosticListenerIStartupFilterObjectPoolProviderIStartup 如果我们需要这些预注册的服务我们可以按照我们熟悉的方式以依赖注入的方式来使用它们。如下面的代码片段所示我们在Startup的Configure方法中直接采用方法注入的方式来使用这些预定义的服务。 1: public class Program 2: { 3: public static void Main(string[] args) 4: { 5: new WebHostBuilder() 6: .UseKestrel() 7: .UseStartupStartup() 8: .Build() 9: .Run(); 10: } 11: } 12: public class Startup 13: { 14: public void Configure( 15: IApplicationBuilder app, 16: IHostingEnvironment environment, 17: ILoggerFactory loggerFactory, 18: IHttpContextFactory httpContextFactory, 19: DiagnosticSource diagnosticSource, 20: DiagnosticListener diagnosticListener) 21: { 22: app.Run(async context 23: { 24: context.Response.ContentType text/html; 25: await context.Response.WriteAsync($IApplicationBuilder{app}br/); 26: await context.Response.WriteAsync($IHostingEnvironment{environment}br/); 27: await context.Response.WriteAsync($ILoggerFactory{loggerFactory}br/); 28: await context.Response.WriteAsync($IHttpContextFactory{httpContextFactory}br/); 29: await context.Response.WriteAsync($DiagnosticSource{diagnosticSource}br/); 30: await context.Response.WriteAsync($DiagnosticListener{diagnosticListener}); 31: }); 32: } 33: } 由于Configure方法注册的Middleware直接将注入服务的注册类型和真实类型的映射关系作为响应内容所以我们访问应用会的得到如下所示的输出结果。 六、ASP.NET Core MVC中的依赖注入 对于ASP.NET MVC 5机器以及之前的版本在默认情况下定义的Controller都具有一个要求那就是Controller类型必须具有一个无参数的默认构造函数否则Controller实例将无法激活。对于自身具有依赖注入功能的ASP.NET Core MVC来说定义Controller将没有了这个限制。对于预注册的服务我们完全可以采用构造器注入的方式在定义的Controller中使用它们。作为演示我们对上面这个应用作了如下的改写。 1: public class Program 2: { 3: public static void Main(string[] args) 4: { 5: new WebHostBuilder() 6: .UseKestrel() 7: .ConfigureServices(servicesservices 8: .AddSingletonIFoo,Foo() 9: .AddSingletonIBar,Bar() 10: .AddMvc()) 11: .Configure(appapp.UseMvc()) 12: .Build() 13: .Run(); 14: } 15: } 16: 17: public class HomeController 18: { 19: public IFoo Foo { get; private set; } 20: public IBar Bar { get; private set; } 21: 22: public HomeController(IFoo foo, IBar bar) 23: { 24: this.Foo foo; 25: this.Bar bar; 26: } 27: 28: [HttpGet(/)] 29: public string Index() 30: { 31: this.HttpContext.Response.ContentType text/html; 32: return $IFoo{this.Foo}br/IBar{this.Bar}; 33: } 34: } 上面这个代码与之前有一个显著的区别那就是我们根本就没有定义Startup类型我们将原本实现在它的两个方法ConfigureServices和Configure中的功能移植到了WebHostBuilder的同名方法中这两种形式的编程方式其实是等效的。在调用ConfigureServices方法的时候我们除了注册MVC相关的服务之外Foo和Bar这两个服务也一并进行了注册。至于另一个Configure方法我们直接调用其扩展方法MVC注册与MVC相关的Middleware。 我们定义了一个默认的HomeController它具有两个类型分别为IFoo和IBar的只读属性后者在构造函数由传入的参数进行初始化我们知道这是构造器注入的编程方式。在Action方法Index中 我们依然将这两个服务的注册类型和真实类型之间的匹配关系作为响应内容所以我们访问这个应用依然会得到如下所示的输出结果。 请扫描此二维码或者搜索“大内老A”关注蒋金楠Artech微信公众帐号你将会得到及时的高质量技术文章推送信息。 内容转载自公众号 大内老A 了解更多