ASP.NET Core中的依赖注入(3): 服务的注册与提供
在采用了依赖注入的应用中,我们总是直接利用DI容器直接获取所需的服务实例,换句话说,DI容器起到了一个服务提供者的角色,它能够根据我们提供的服务描述信息提供一个可用的服务对象。ASP.NET Core中的DI容器体现为一个实现了IServiceProvider接口的对象。
一、ServiceProvider与ServiceDescriptor我一直觉得优秀的设计首先应该是简单的设计,至少是看起来简单的设计,这就是我们所谓的大道至简。作为一个服务的提供者,ASP.NET Core中的DI容器最终体现为一个IServiceProvider接口,我们将所有实现了该接口的类型及其实例统称为ServiceProvider。如下面的代码片段所示,该接口简单至极,它仅仅提供了唯一个GetService方法,该方法根据提供的服务类型为你提供对应的服务实例。 1: public?interface IServiceProvider 2: {
3:???? object GetService(Type serviceType); 4: }
ASP.NET Core内部真正使用的是一个实现了IServiceProvider接口的内部类型(该类型的名称为“ServiceProvider”),我们不能直接创建该对象,只能间接地通过调用IServiceCollection接口的扩展方法BuildServiceProvider得到它。IServiceCollection接口定义在“Microsoft.Extensions.DependencyInjection”命名空间下,如果没有特别说明,本系列文章涉及到的与ASP.NET Core依赖注入相关的类型均采用此命名空间。 如下面的代码片段所示,IServiceCollection接口实际上代表一个元素为ServiceDescriptor对象的集合,它直接继承了另一个接口IList<ServiceDescriptor>,而ServiceCollection类实现了该接口。 static IServiceProvider BuildServiceProvider(this IServiceCollection services);
5:?
6: interface IServiceCollection : IList<ServiceDescriptor> 7: {}
8:?
9: Public class ServiceCollection: IServiceCollection 10: {
11:???? //省略成员 12: }
体现为DI容器的ServiceProvider之所以能够根据我们给定的服务类型(一般是一个接口类型)提供一个能够开箱即用的服务实例,是因为我们预先注册了相应的服务描述信息,这些指导ServiceProvider正确实施服务提供操作的服务描述体现为如下一个ServiceDescriptor类型。 public ServiceDescriptor(Type serviceType,object instance);
5:???? 6:?
8:???? public ServiceLifetime???????????????????? Lifetime {? get; }
10:???? public Type??????????????????????????????? ImplementationType {? get; }
12:???? public Func<IServiceProvider,1)">object>????? ImplementationFactory {? get; }?????
13: }
ServiceDescriptor的ServiceType属性代表提供服务的生命类型,由于标准化的服务一般会定义成接口,所以在绝大部分情况下体现为一个接口类型。类型为ServiceLifetime的属性Lifetime体现了ServiceProvider针对服务实例生命周期的控制方式。如下面的代码片段所示,ServiceLifetime是一个美剧类型,定义其中的三个选项(Singleton、Scoped和Transient)体现三种对服务对象生命周期的控制形式,我们将在本节后续部分对此作专门的介绍。 3:???? Singleton,
6: } 由于ASP.NET Core中的ServiceProvider是根据一个代表ServiceDescriptor集合的IServiceCollection对象创建的,当我们调用其GetService方法的时候,它会根据我们提供的服务类型找到对应的ServiceDecriptor对象。如果该ServiceDecriptor对象的ImplementationInstance属性返回一个具体的对象,该对象将直接用作被提供的服务实例。如果ServiceDecriptor对象的ImplementationFactory返回一个具体的委托,该委托对象将直接用作创建服务实例的工厂。 如果这两个属性均为Null,ServiceProvider才会根据ImplementationType属性返回的类型调用相应的构造函数创建被提供的服务实例。至于我们在上面一节中提到的三种依赖注入方式,ServiceProvider仅仅支持构造器注入,属性注入和方法注入的支持并未提供。 二、服务的注册与提供ASP.NET Core针对依赖注入的编程主要体现在两个方面:其一,创建一个ServiceCollection对象并将服务注册信息以ServiceDescriptor对象的形式添加其中;其二,针对ServiceCollection对象创建对应的ServiceProvider并利用它提供我们需要的服务实例。 在进行服务注册的时候,我们可以直接调用相应的构造函数创建ServiceDescriptor对象并将其添加到ServiceCollection对象之中。除此之外,IServiceCollection接口还具有如下三组扩展方法将这两个步骤合二为一。从下面给出的代码片段我们不难看出这三组扩展方法分别针对上面我们提及的三种针对服务实例的生命周期控制方式,泛型参数TService代表服务的声明类型,即ServiceDescriptor的ServiceType属性,至于ServiceDescriptor的其他属性,则通过方法相应的参数来提供。 4:??? //其他AddScoped<TService>重载
9:???? static IServiceCollection AddTransient<TService>(//其他AddTransient<TService>重载 class ServiceProviderExtensions static T GetService<T>(this IServiceProvider provider);
static T GetRequiredService<T>( 6: }
利用ServiceProvider来提供服务接下来采用实例演示的方式来介绍如何利用ServiceCollection进行服务注册,以及如何利用ServiceCollection创建对应的ServiceProvider来提供我们需要的服务实例。我们创建一个ASP.NET Core控制台程序,并在project.json中按照如下的方式添加针对 “Microsoft.Extensions.DepedencyInjection”这个NuGet包的依赖。 2:?? "dependencies": {
4:?? },1)"> 5:?? ... interface IFoo {} 3: interface IBaz {}
5: { 7:???? IBar Bar { get; } 9: } 11: class Foo : IFoo {}
13: class Baz : IBaz {}
14: class Gux : IGux 15: {
16:???? public IFoo Foo { get; private set; } 17:???? public IBar Bar { get; 18:???? public IBaz Baz { get; 19:? 20:???? public Gux(IFoo foo,IBar bar,IBaz baz) 21:???? {
22:???????? this.Foo = foo; 23:???????? this.Bar = bar; 24:???????? this.Baz = baz; 25:???? }
26: }???
现在我们在作为程序入口的Main方法中创建了一个ServiceCollection对象,并采用不同的方式完成了针对四个服务接口的注册。具体来说,对于正对服务接口IFoo和IGux的ServiceDescriptor来说,我们指定了代表服务真实类型的ImplementationType属性,而对于针对服务接口IBar和IBaz的ServiceDescriptor来说,我们初始化的则是分别代表服务实例和服务工厂的ImplementationInstance个ImplementationFactory属性。由于我们调用的是AddSingleton方法,所以四个ServiceDescriptor的Lifetime属性均为Singleton。 void Main(string[] args)
5:???????? IServiceCollection services = new ServiceCollection()
7:???????????? .AddSingleton<IBar>(new Bar())
9:???????????? .AddSingleton<IGux,Gux>(); 12:???????? Console.WriteLine("serviceProvider.GetService<IFoo>(): {0}",serviceProvider.GetService<IFoo>());
14:???????? Console.WriteLine("serviceProvider.GetService<IBaz>(): {0}",serviceProvider.GetService<IBaz>());
16:???? } 1: serviceProvider.GetService<IFoo>(): Foo 3: serviceProvider.GetService<IBaz>(): Baz static IEnumerable<T> GetServices<T>(static IEnumerable<object> GetServices( 5: } 值得一提的是,如果ServiceProvider所在的ServiceCollection包含多个具有相同服务类型(对应ServiceType属性)的ServiceDescriptor,当我们调用GetService方法获取单个服务实例的时候,只有最后一个ServiceDescriptor才是有效的,至于其他的ServiceDescriptor,它们只有在获取服务集合的场景下才有意义。 我们通过一个简单的实例来演示如何利用ServiceProvider得到一个包含多个服务实例的集合。我们在一个控制台应用中定义了如下一个服务接口IFoobar,两个服务类型Foo和Bar均实现了这个接口。在作为程序入口的Main方法中,我们将针针对服务类型Foo和Bar的两个ServiceDescriptor添加到创建的ServiceCollection对象中,这两个ServiceDescriptor对象的ServiceType属性均为IFoobar。 9:???????? IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
11:? 13:???????? int index = 1;
15:???????? foreach (IFoobar foobar in services) 17:???????????? Console.WriteLine("{0}: {1}",index++,foobar);
19:???? } 21:? 23: class Foo : IFoobar {}
1: serviceProvider.GetService<IFoobar>(): Bar 3: 1: Foo 5:???????? IServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider();
7:???????? Debug.Assert( 8:???? }
6:???????????? .AddTransient<IFoo,1)"> 7:???????????? .AddTransient<IBar,Bar>() 9:???????????? .BuildServiceProvider(); ,IBar>>().Bar); 14: } 16: interface IFoobar<T1,T2>
18:???? T1 Foo { get; } 21: 23:?
26:???? public T1 Foo { get; 27:???? public T2 Bar { get; 28:???? public Foobar(T1 foo,T2 bar) 29:???? {
30:???????? 31:???????? 32:???? } 33: }
34: 35: class Bar : IBar {} 在作为入口程序的Main方法中,我们创建了一个ServiceCollection对象并采用Transient模式注册了上述三个服务接口与对应实现类型之间的映射关系,对于泛型服务IFoobar<T1,T2>/Foobar<T1,T2>来说,我们指定的是不携带具体泛型参数的开放泛型类型IFoobar<,>/Foobar<,>。利用此ServiceCollection创建出对应的ServiceProvider之后,我们调用后者的GetService方法并指定IFoobar<IFoo,IBar>为服务类型。得到的服务对象将会是一个Foobar<Foo,Bar>对象,我们将它的Foo和Bar属性类型输出于控制台上作为验证。该程序执行之后将会在控制台上产生下所示的输出结果。 2: serviceProvider.GetService<IFoobar<IFoo,IBar>>().Bar: Bar
ASP.NET Core中的依赖注入(1):控制反转(IoC) (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- asp.net-mvc – 模拟FormsAuthentication.Authen
- asp.net-mvc – 当我使用Validator.TryValidateO
- asp.net-mvc – ASP.NET MVC自动解码来自AJAX的J
- asp.net-mvc-3 – outputcache mvc3只注销了用户
- .Net Core技术研究-WebApi迁移ASP.NET Core2.0
- 禁用ASP.Net ReportViewer控件上的分页
- asp.net-mvc – RavenDB Ids和ASP.NET MVC3路由
- asp.net-mvc-3 – Asp.net MVC3从razor View访问
- asp.net-mvc – 在web api 2中使用autofac的无参
- asp.net – Telerik RadAjaxManager仍然回发