接下来我们调用ServiceCollection对象的扩展方法BuildServiceProvider得到对应的ServiceProvider对象,然后调用其扩展方法GetService<T>分别获得针对四个接口的服务实例对象并将类型名称其输出到控制台上。运行该程序之后,我们会在控制台上得到如下的输出结果,由此印证ServiceProvider为我们提供了我们期望的服务实例。
serviceProvider.GetService<IFoo>(): Foo serviceProvider.GetService<IBar>(): Bar serviceProvider.GetService<IBaz>(): Baz serviceProvider.GetService<IGux>(): Gux
提供一个服务实例的集合
如果我们在调用GetService方法的时候将服务类型指定为IEnumerable<T>,那么返回的结果将会是一个集合对象。除此之外, 我们可以直接调用IServiceProvider如下两个扩展方法GetServeces达到相同的目的。在这种情况下,ServiceProvider将会利用所有与指定服务类型相匹配的ServiceDescriptor来提供具体的服务实例,这些均会作为返回的集合对象的元素。如果所有的ServiceDescriptor均与指定的服务类型不匹配,那么最终返回的是一个空的集合对象。
public static class ServiceProviderExtensions { public static IEnumerable<T> GetServices<T>(this IServiceProvider provider); public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType); }
值得一提的是,如果ServiceProvider所在的ServiceCollection包含多个具有相同服务类型(对应ServiceType属性)的ServiceDescriptor,当我们调用GetService方法获取单个服务实例的时候,只有最后一个ServiceDescriptor才是有效的,至于其他的ServiceDescriptor,它们只有在获取服务集合的场景下才有意义。
我们通过一个简单的实例来演示如何利用ServiceProvider得到一个包含多个服务实例的集合。我们在一个控制台应用中定义了如下一个服务接口IFoobar,两个服务类型Foo和Bar均实现了这个接口。在作为程序入口的Main方法中,我们将针针对服务类型Foo和Bar的两个ServiceDescriptor添加到创建的ServiceCollection对象中,这两个ServiceDescriptor对象的ServiceType属性均为IFoobar。
class Program { static void Main(string[] args) { IServiceCollection serviceCollection = new ServiceCollection() .AddSingleton<IFoobar, Foo>() .AddSingleton<IFoobar, Bar>(); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService<IFoobar>(): {0}", serviceProvider.GetService<IFoobar>()); IEnumerable<IFoobar> services = serviceProvider.GetServices<IFoobar>(); int index = 1; Console.WriteLine("serviceProvider.GetServices<IFoobar>():"); foreach (IFoobar foobar in services) { Console.WriteLine("{0}: {1}", index++, foobar); } } } public interface IFoobar {} public class Foo : IFoobar {} public class Bar : IFoobar {}
在调用ServiceCollection对象的扩展方法BuildServiceProvider得到对应的ServiceProvider对象之后,我们先调用其GetService<T>方法以确定针对服务接口IFoobar得到的服务实例的真实类型就是是Foo还是Bar。接下来我们调用ServiceProvider的扩展方法GetServices<T>获取一组针对服务接口IFoobar的服务实例并将它们的真是类型打印在控制台上。该程序运行后将会在控制台上生成如下的输出结果。
serviceProvider.GetService<IFoobar>(): Bar serviceProvider.GetServices<IFoobar>(): 1: Foo 2: Bar
获取ServiceProvider自身对象
对于ServiceProvider的服务提供机制来说,还有一个小小的细节值得我们关注,那就是当我们调用GetService或者GetRequiredService方法的时候若将服务类型设定为IServiceProvider,那么得到的对象实际上就是ServiceProvider自身这个对象。与之同理,调用GetServices方法将会返回一个包含自身的集合。如下所示的代码片段体现了ServiceProvider的这个特性。
class Program { static void Main(string[] args) { IServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider(); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetService<IServiceProvider>())); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetServices<IServiceProvider>().Single())); } }
对泛型的支持
ServiceProvider提供的服务实例不仅限于普通的类型,它对泛型服务类型同样支持。在针对泛型服务进行注册的时候,我们可以将服务类型设定为携带具体泛型参数的“关闭泛型类型”(比如IFoobar<IFoo,IBar>),除此之外服务类型也可以是包含具体泛型参数的“开放泛型类型”(比如IFoo<,>)。前者实际上还是将其视为非泛型服务来对待,后者才真正体现了“泛型”的本质。