二、使用.NET Core ID容器

在依赖注入容器中,可以在应用程序中有一个位置,在其中定义什么协定映射到哪个特定的实现上,还可以指定是应该将服务作为一个单例来使用,还是应该在每次使用时创建一个新实例。

在下一个示例中,使用前面创建的GreetingService来实现IGreetingService和HomeController类,但这次我们使用依赖注入容器。

示例WithDIContainer使用了如下NuGet包和名称空间:

Microsoft.Extensions.DependencyInjection

名称空间

System

Microsoft.Extensions.DependencyInjection

在Program类中,现在定义RegisterServices方法。在这里,实例化一个新的ServiceCollection对象。在添加了NuGet包Microsoft.Extensions.DependencyInjection之后,ServiceCollection就在名称空间Microsoft.Extensions.DependencyInjection中定义。使用AddSingleton和AddTransient时,ServicceCollection的扩展方法用来注册DI容器需要知道的类型。在示例应用程序中,GreetingService和HomeController都在容器中注册,这允许从容器中检索HomeController。

当请求IGreetingService接口时,会实例化GreetingService类。HomeController本身没有实现接口。通过这个DI容器设置,当请求HomeController时,会实例化HomeController。DI容器配置还定义了服务的生命周期。对于GreetingService,请求IGreetingService时总是返回相同的实例。这和HomeController是不同的。对于HomeController,每次请求检索HomeController时,都会创建一个新的实例。使用AddSingleton和AddTransient方法指定服务的生命周期信息。本章后面将介绍这些服务的生命周期。

调用方法BuildServiceProvider会返回一个ServiceProvider对象,该对象可以用来访问已注册的服务:

        static ServiceProvider RegisterServices()
        {
            var services = new ServiceCollection();
            services.AddSingleton();
            services.AddTransient();
            return services.BuildServiceProvider();
        }

注意:

在.NET Core 1.1中,BuildServiceProvider返回接口IServiceProvider,而不是返回具体的类。在.NET Core 1.1中,ServiceProvider声明为internal,因此只能通过它的公共接口IServiceProvider从外部使用。在.NET Core 2.0中修改了实现方式,使ServideProivder类变成public,并更改BuildServiceProvider的方法声明,以返回ServiceProvider。这允许直接访问ServiceProvider的Dispose方法,而不需要将返回的对象转换为IDisposable来调用Dispose。

注意:

如果多次将相同的接口合同添加到服务集合中,最后一个增加的接口合同就会从容器中获得接口。如果需要一些其他功能,比如使用ASP.NET Core或Entity Framework Core实现的服务,就很容易用不同的实现替换协定。另一方面,对于ServiceCollection类,还可以删除服务,并为特定的协定检索所有服务的列表。

接下来,修改Main()方法来调用RegisterService方法,以便在DI容器中注册,然后调用ServiceProvider的GetRequiredService方法,来获取对HomeController实例的引用:

        static void Main(string[] args)
        {
            using (ServiceProvider container = RegisterServices())
            {
                var controller = container.GetRequiredService();
                string result = controller.Hello("Katharina");
                Console.WriteLine(result);
            }
        

注意:

在ServiceProvider类中,存在GetService和GetRequiredService的不同重载,在ServiceProvider类中直接实现的方法是带有Type参数的GetService。泛型方法GetService是一个扩展方法,它采用泛型类型参数,并将其传递给GetService方法。

如果该服务在容器中不可用,则GetService返回null。扩展方法GetRequiredService检查到null结果,如果未找到服务,就抛出一个InvalidOperationException异常。如果服务提供程序实现了接口ISupportsRequiredService,扩展方法GetRequiredService将调用提供程序的GetRequiredService。.NET Core 2.0的容器没有实现这个接口,但是一些第三方的容器实现了。

启动应用程序时,在GetRequiredService方法的请求下,DI容器将创建HomeController类的实例。HomeController构造函数需要一个实现IGreetingService的对象。这个接口也在容器中注册;对于IGreetingService,需要返回GreetingService对象。GreetingService类有一个默认的构造函数,因此容器可以创建一个实例,并将该实例传递给HomeController的构造函数。这个实例与控制器变量一起使用,与以前一样用来调用Hello方法。

如果不是每个依赖项都在DI容器中注册,会发生什么情况?要查看此操作,可以使用DI容器删除IGreetingService的配置。在这种情况下,容器会抛出InvalidOperationException异常。此错误消息显示:在尝试激活WithDiContainer.HomeController时无法解析WithDIContainer.IGreetingService类型的服务。

你可能感兴趣的:(二、使用.NET Core ID容器)