ASP.NET Core针对依赖注入的编程主要体现在两个方面:其一,创建一个ServiceCollection对象并将服务注册信息以ServiceDescriptor对象的形式添加其中;其二,针对ServiceCollection对象创建对应的ServiceProvider并利用它提供我们需要的服务实例。
在进行服务注册的时候,我们可以直接调用相应的构造函数创建ServiceDescriptor对象并将其添加到ServiceCollection对象之中。除此之外,IServiceCollection接口还具有如下三组扩展方法将这两个步骤合二为一。从下面给出的代码片段我们不难看出这三组扩展方法分别针对上面我们提及的三种针对服务实例的生命周期控制方式,泛型参数TService代表服务的声明类型,即ServiceDescriptor的ServiceType属性,至于ServiceDescriptor的其他属性,则通过方法相应的参数来提供。
public static class ServiceCollectionExtensions { public static IServiceCollection AddScoped<TService>(this IServiceCollection services) where TService: class; //其他AddScoped<TService>重载 public static IServiceCollection AddSingleton<TService>(this IServiceCollection services) where TService: class; //其他AddSingleton<TService>重载 public static IServiceCollection AddTransient<TService>(this IServiceCollection services) where TService: class; //其他AddTransient<TService>重载 }
对于用作DI容器的ServiceProvider对象来说,我们可以直接调用它的GetService方法根据指定的服务类型获得想用的服务实例。除此之外,服务的提供还可以通过IServiceProvider接口相应的扩展方法来完成。如下面的代码片段所示,扩展方法GetService<T>以泛型参数的形式指定服务的声明类型。至于另外两个扩展方法GetRequiredService和GetRequiredService<T>,如果ServiceProvider不能提供一个具体的服务实例,一个InvalidOperationException异常会被抛出来并提示相应的服务注册信息不足。
public static class ServiceProviderExtensions { public static T GetService<T>(this IServiceProvider provider); public static object GetRequiredService(this IServiceProvider provider, Type serviceType); public static T GetRequiredService<T>(this IServiceProvider provider); }
利用ServiceProvider来提供服务
接下来采用实例演示的方式来介绍如何利用ServiceCollection进行服务注册,以及如何利用ServiceCollection创建对应的ServiceProvider来提供我们需要的服务实例。我们创建一个ASP.NET Core控制台程序,并在project.json中按照如下的方式添加针对 “Microsoft.Extensions.DepedencyInjection”这个NuGet包的依赖。
{ "dependencies": { "Microsoft.Extensions.DependencyInjection": "1.0.0-rc1-final" }, ... }
我们接下来定义四个服务接口(IFoo、IBar、IBaz和IGux)以及分别实现它们的四个服务类(Foo、Bar、Baz和Gux)如下面的代码片段所示,IGux具有三个只读属性(Foo、Bar和Baz)均为接口类型,并在构造函数中进行初始化。
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; } }
现在我们在作为程序入口的Main方法中创建了一个ServiceCollection对象,并采用不同的方式完成了针对四个服务接口的注册。具体来说,对于正对服务接口IFoo和IGux的ServiceDescriptor来说,我们指定了代表服务真实类型的ImplementationType属性,而对于针对服务接口IBar和IBaz的ServiceDescriptor来说,我们初始化的则是分别代表服务实例和服务工厂的ImplementationInstance个ImplementationFactory属性。由于我们调用的是AddSingleton方法,所以四个ServiceDescriptor的Lifetime属性均为Singleton。
class Program { static void Main(string[] args) { IServiceCollection services = new ServiceCollection() .AddSingleton<IFoo, Foo>() .AddSingleton<IBar>(new Bar()) .AddSingleton<IBaz>(_ => new Baz()) .AddSingleton<IGux, Gux>(); IServiceProvider serviceProvider = services.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService<IFoo>(): {0}",serviceProvider.GetService<IFoo>()); Console.WriteLine("serviceProvider.GetService<IBar>(): {0}", serviceProvider.GetService<IBar>()); Console.WriteLine("serviceProvider.GetService<IBaz>(): {0}", serviceProvider.GetService<IBaz>()); Console.WriteLine("serviceProvider.GetService<IGux>(): {0}", serviceProvider.GetService<IGux>()); } }