小編給大家分享一下ASP.NET Core依賴注入之服務(wù)注冊與提供的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
創(chuàng)新互聯(lián)專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都做網(wǎng)站、網(wǎng)站設(shè)計(jì)、外貿(mào)營銷網(wǎng)站建設(shè)、鐵門關(guān)網(wǎng)絡(luò)推廣、成都微信小程序、鐵門關(guān)網(wǎng)絡(luò)營銷、鐵門關(guān)企業(yè)策劃、鐵門關(guān)品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營等,從售前售中售后,我們都將竭誠為您服務(wù),您的肯定,是我們大的嘉獎;創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供鐵門關(guān)建站搭建服務(wù),24小時服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com前言
在采用了依賴注入的應(yīng)用中,我們總是直接利用DI容器直接獲取所需的服務(wù)實(shí)例,換句話說,DI容器起到了一個服務(wù)提供者的角色,它能夠根據(jù)我們提供的服務(wù)描述信息提供一個可用的服務(wù)對象。ASP.NET Core中的DI容器體現(xiàn)為一個實(shí)現(xiàn)了IServiceProvider接口的對象。
ServiceProvider與ServiceDescriptor
服務(wù)的注冊與提供
利用ServiceProvider來提供服務(wù)
提供一個服務(wù)實(shí)例的集合
獲取ServiceProvider自身對象
對泛型的支持
一、ServiceProvider與ServiceDescriptor
我一直覺得優(yōu)秀的設(shè)計(jì)首先應(yīng)該是簡單的設(shè)計(jì),至少是看起來簡單的設(shè)計(jì),這就是我們所謂的大道至簡。作為一個服務(wù)的提供者,ASP.NET Core中的DI容器最終體現(xiàn)為一個IServiceProvider接口,我們將所有實(shí)現(xiàn)了該接口的類型及其實(shí)例統(tǒng)稱為ServiceProvider。如下面的代碼片段所示,該接口簡單至極,它僅僅提供了個GetService方法,該方法根據(jù)提供的服務(wù)類型為你提供對應(yīng)的服務(wù)實(shí)例。
public interface IServiceProvider { object GetService(Type serviceType); }
ASP.NET Core內(nèi)部真正使用的是一個實(shí)現(xiàn)了IServiceProvider接口的內(nèi)部類型(該類型的名稱為“ServiceProvider”),我們不能直接創(chuàng)建該對象,只能間接地通過調(diào)用IServiceCollection接口的擴(kuò)展方法BuildServiceProvider得到它。IServiceCollection接口定義在“Microsoft.Extensions.DependencyInjection”命名空間下,如果沒有特別說明,本系列文章涉及到的與ASP.NET Core依賴注入相關(guān)的類型均采用此命名空間。 如下面的代碼片段所示,IServiceCollection接口實(shí)際上代表一個元素為ServiceDescriptor對象的集合,它直接繼承了另一個接口IList
public static class ServiceCollectionExtensions { public static IServiceProvider BuildServiceProvider(this IServiceCollection services); } public interface IServiceCollection : IList{} Public class ServiceCollection: IServiceCollection { //省略成員 }
體現(xiàn)為DI容器的ServiceProvider之所以能夠根據(jù)我們給定的服務(wù)類型(一般是一個接口類型)提供一個能夠開箱即用的服務(wù)實(shí)例,是因?yàn)槲覀冾A(yù)先注冊了相應(yīng)的服務(wù)描述信息,這些指導(dǎo)ServiceProvider正確實(shí)施服務(wù)提供操作的服務(wù)描述體現(xiàn)為如下一個ServiceDescriptor類型。
public class ServiceDescriptor { public ServiceDescriptor(Type serviceType, object instance); public ServiceDescriptor(Type serviceType, Funcfactory, ServiceLifetime lifetime); public ServiceDescriptor(Type serviceType, Type implementationType, ServiceLifetime lifetime); public Type ServiceType { get; } public ServiceLifetime Lifetime { get; } public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func ImplementationFactory { get; } }
ServiceDescriptor的ServiceType屬性代表提供服務(wù)的生命類型,由于標(biāo)準(zhǔn)化的服務(wù)一般會定義成接口,所以在絕大部分情況下體現(xiàn)為一個接口類型。類型為ServiceLifetime的屬性Lifetime體現(xiàn)了ServiceProvider針對服務(wù)實(shí)例生命周期的控制方式。如下面的代碼片段所示,ServiceLifetime是一個美劇類型,定義其中的三個選項(xiàng)(Singleton、Scoped和Transient)體現(xiàn)三種對服務(wù)對象生命周期的控制形式,我們將在本節(jié)后續(xù)部分對此作專門的介紹。
public enum ServiceLifetime { Singleton, Scoped, Transient }
3-10對于ServiceDescriptor的其他三個屬性來說,它們實(shí)際上是輔助ServiceProvider完成具體的服務(wù)實(shí)例提供操。ImplementationType屬性代表被提供服務(wù)實(shí)例的真實(shí)類型,屬性ImplementationInstance則直接代表被提供的服務(wù)實(shí)例,ImplementationFactory則提供了一個創(chuàng)建服務(wù)實(shí)例的委托對象。ASP.NET Core與依賴注入相關(guān)的幾個核心類型具有如圖10所示的關(guān)系。
由于ASP.NET Core中的ServiceProvider是根據(jù)一個代表ServiceDescriptor集合的IServiceCollection對象創(chuàng)建的,當(dāng)我們調(diào)用其GetService方法的時候,它會根據(jù)我們提供的服務(wù)類型找到對應(yīng)的ServiceDecriptor對象。如果該ServiceDecriptor對象的ImplementationInstance屬性返回一個具體的對象,該對象將直接用作被提供的服務(wù)實(shí)例。如果ServiceDecriptor對象的ImplementationFactory返回一個具體的委托,該委托對象將直接用作創(chuàng)建服務(wù)實(shí)例的工廠。
如果這兩個屬性均為Null,ServiceProvider才會根據(jù)ImplementationType屬性返回的類型調(diào)用相應(yīng)的構(gòu)造函數(shù)創(chuàng)建被提供的服務(wù)實(shí)例。至于我們在上面一節(jié)中提到的三種依賴注入方式,ServiceProvider僅僅支持構(gòu)造器注入,屬性注入和方法注入的支持并未提供。
二、服務(wù)的注冊與提供
ASP.NET Core針對依賴注入的編程主要體現(xiàn)在兩個方面:其一,創(chuàng)建一個ServiceCollection對象并將服務(wù)注冊信息以ServiceDescriptor對象的形式添加其中;其二,針對ServiceCollection對象創(chuàng)建對應(yīng)的ServiceProvider并利用它提供我們需要的服務(wù)實(shí)例。
在進(jìn)行服務(wù)注冊的時候,我們可以直接調(diào)用相應(yīng)的構(gòu)造函數(shù)創(chuàng)建ServiceDescriptor對象并將其添加到ServiceCollection對象之中。除此之外,IServiceCollection接口還具有如下三組擴(kuò)展方法將這兩個步驟合二為一。從下面給出的代碼片段我們不難看出這三組擴(kuò)展方法分別針對上面我們提及的三種針對服務(wù)實(shí)例的生命周期控制方式,泛型參數(shù)TService代表服務(wù)的聲明類型,即ServiceDescriptor的ServiceType屬性,至于ServiceDescriptor的其他屬性,則通過方法相應(yīng)的參數(shù)來提供。
public static class ServiceCollectionExtensions { public static IServiceCollection AddScoped(this IServiceCollection services) where TService: class; //其他AddScoped 重載 public static IServiceCollection AddSingleton (this IServiceCollection services) where TService: class; //其他AddSingleton 重載 public static IServiceCollection AddTransient (this IServiceCollection services) where TService: class; //其他AddTransient 重載 }
對于用作DI容器的ServiceProvider對象來說,我們可以直接調(diào)用它的GetService方法根據(jù)指定的服務(wù)類型獲得想用的服務(wù)實(shí)例。除此之外,服務(wù)的提供還可以通過IServiceProvider接口相應(yīng)的擴(kuò)展方法來完成。如下面的代碼片段所示,擴(kuò)展方法GetService
public static class ServiceProviderExtensions { public static T GetService(this IServiceProvider provider); public static object GetRequiredService(this IServiceProvider provider, Type serviceType); public static T GetRequiredService (this IServiceProvider provider); }
利用ServiceProvider來提供服務(wù)
接下來采用實(shí)例演示的方式來介紹如何利用ServiceCollection進(jìn)行服務(wù)注冊,以及如何利用ServiceCollection創(chuàng)建對應(yīng)的ServiceProvider來提供我們需要的服務(wù)實(shí)例。我們創(chuàng)建一個ASP.NET Core控制臺程序,并在project.json中按照如下的方式添加針對 “Microsoft.Extensions.DepedencyInjection”這個NuGet包的依賴。
{ "dependencies": { "Microsoft.Extensions.DependencyInjection": "1.0.0-rc1-final" }, ... }
我們接下來定義四個服務(wù)接口(IFoo、IBar、IBaz和IGux)以及分別實(shí)現(xiàn)它們的四個服務(wù)類(Foo、Bar、Baz和Gux)如下面的代碼片段所示,IGux具有三個只讀屬性(Foo、Bar和Baz)均為接口類型,并在構(gòu)造函數(shù)中進(jìn)行初始化。
public interface IFoo {} public interface IBar {} public interface IBaz {} public interface IGux { IFoo Foo { get; } IBar Bar { get; } IBaz Baz { get; } } public class Foo : IFoo {} public class Bar : IBar {} public class Baz : IBaz {} public class Gux : IGux { public IFoo Foo { get; private set; } public IBar Bar { get; private set; } public IBaz Baz { get; private set; } public Gux(IFoo foo, IBar bar, IBaz baz) { this.Foo = foo; this.Bar = bar; this.Baz = baz; } }
現(xiàn)在我們在作為程序入口的Main方法中創(chuàng)建了一個ServiceCollection對象,并采用不同的方式完成了針對四個服務(wù)接口的注冊。具體來說,對于正對服務(wù)接口IFoo和IGux的ServiceDescriptor來說,我們指定了代表服務(wù)真實(shí)類型的ImplementationType屬性,而對于針對服務(wù)接口IBar和IBaz的ServiceDescriptor來說,我們初始化的則是分別代表服務(wù)實(shí)例和服務(wù)工廠的ImplementationInstance個ImplementationFactory屬性。由于我們調(diào)用的是AddSingleton方法,所以四個ServiceDescriptor的Lifetime屬性均為Singleton。
class Program { static void Main(string[] args) { IServiceCollection services = new ServiceCollection() .AddSingleton() .AddSingleton (new Bar()) .AddSingleton (_ => new Baz()) .AddSingleton (); IServiceProvider serviceProvider = services.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService (): {0}",serviceProvider.GetService ()); Console.WriteLine("serviceProvider.GetService (): {0}", serviceProvider.GetService ()); Console.WriteLine("serviceProvider.GetService (): {0}", serviceProvider.GetService ()); Console.WriteLine("serviceProvider.GetService (): {0}", serviceProvider.GetService ()); } }
接下來我們調(diào)用ServiceCollection對象的擴(kuò)展方法BuildServiceProvider得到對應(yīng)的ServiceProvider對象,然后調(diào)用其擴(kuò)展方法GetService
serviceProvider.GetService(): Foo serviceProvider.GetService (): Bar serviceProvider.GetService (): Baz serviceProvider.GetService (): Gux
提供一個服務(wù)實(shí)例的集合
如果我們在調(diào)用GetService方法的時候?qū)⒎?wù)類型指定為IEnumerable
public static class ServiceProviderExtensions { public static IEnumerableGetServices (this IServiceProvider provider); public static IEnumerable
值得一提的是,如果ServiceProvider所在的ServiceCollection包含多個具有相同服務(wù)類型(對應(yīng)ServiceType屬性)的ServiceDescriptor,當(dāng)我們調(diào)用GetService方法獲取單個服務(wù)實(shí)例的時候,只有最后一個ServiceDescriptor才是有效的,至于其他的ServiceDescriptor,它們只有在獲取服務(wù)集合的場景下才有意義。
我們通過一個簡單的實(shí)例來演示如何利用ServiceProvider得到一個包含多個服務(wù)實(shí)例的集合。我們在一個控制臺應(yīng)用中定義了如下一個服務(wù)接口IFoobar,兩個服務(wù)類型Foo和Bar均實(shí)現(xiàn)了這個接口。在作為程序入口的Main方法中,我們將針針對服務(wù)類型Foo和Bar的兩個ServiceDescriptor添加到創(chuàng)建的ServiceCollection對象中,這兩個ServiceDescriptor對象的ServiceType屬性均為IFoobar。
class Program { static void Main(string[] args) { IServiceCollection serviceCollection = new ServiceCollection() .AddSingleton() .AddSingleton (); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService (): {0}", serviceProvider.GetService ()); IEnumerable services = serviceProvider.GetServices (); int index = 1; Console.WriteLine("serviceProvider.GetServices ():"); foreach (IFoobar foobar in services) { Console.WriteLine("{0}: {1}", index++, foobar); } } } public interface IFoobar {} public class Foo : IFoobar {} public class Bar : IFoobar {}
在調(diào)用ServiceCollection對象的擴(kuò)展方法BuildServiceProvider得到對應(yīng)的ServiceProvider對象之后,我們先調(diào)用其GetService
serviceProvider.GetService(): Bar serviceProvider.GetServices (): 1: Foo 2: Bar
獲取ServiceProvider自身對象
對于ServiceProvider的服務(wù)提供機(jī)制來說,還有一個小小的細(xì)節(jié)值得我們關(guān)注,那就是當(dāng)我們調(diào)用GetService或者GetRequiredService方法的時候若將服務(wù)類型設(shè)定為IServiceProvider,那么得到的對象實(shí)際上就是ServiceProvider自身這個對象。與之同理,調(diào)用GetServices方法將會返回一個包含自身的集合。如下所示的代碼片段體現(xiàn)了ServiceProvider的這個特性。
class Program { static void Main(string[] args) { IServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider(); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetService())); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetServices ().Single())); } }
對泛型的支持
ServiceProvider提供的服務(wù)實(shí)例不僅限于普通的類型,它對泛型服務(wù)類型同樣支持。在針對泛型服務(wù)進(jìn)行注冊的時候,我們可以將服務(wù)類型設(shè)定為攜帶具體泛型參數(shù)的“關(guān)閉泛型類型”(比如IFoobar
比如我們注冊了某個泛型服務(wù)接口IFoobar<,>與它的實(shí)現(xiàn)類Foobar<,>之間的映射關(guān)系,當(dāng)我們指定一個攜帶具體泛型參數(shù)的服務(wù)接口類型IFoobar
我們同樣利用一個簡單的控制臺應(yīng)用來演示基于泛型的服務(wù)注冊與提供方式。如下面的代碼片段所示,我們定義了三個服務(wù)接口(IFoo、IBar和IFoobar
class Program { static void Main(string[] args) { IServiceProvider serviceProvider = new ServiceCollection() .AddTransient() .AddTransient () .AddTransient(typeof(IFoobar<,>), typeof(Foobar<,>)) .BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService >().Foo: {0}", serviceProvider.GetService >().Foo); 12: Console.WriteLine("serviceProvider.GetService >().Bar: {0}", serviceProvider.GetService >().Bar); } } public interface IFoobar { T1 Foo { get; } T2 Bar { get; } } public interface IFoo {} public interface IBar {} public class Foobar : IFoobar { public T1 Foo { get; private set; } public T2 Bar { get; private set; } public Foobar(T1 foo, T2 bar) { this.Foo = foo; this.Bar = bar; } } public class Foo : IFoo {} public class Bar : IBar {}
在作為入口程序的Main方法中,我們創(chuàng)建了一個ServiceCollection對象并采用Transient模式注冊了上述三個服務(wù)接口與對應(yīng)實(shí)現(xiàn)類型之間的映射關(guān)系,對于泛型服務(wù)IFoobar
serviceProvider.GetService>().Foo: Foo serviceProvider.GetService >().Bar: Bar
以上是“ASP.NET Core依賴注入之服務(wù)注冊與提供的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!