Autofac DI容器
在本章中
- 使用Autofac的基本注册API工作
- 管理组件的寿命
- 配置复杂的API
- 配置序列、装饰器和组合物
在前几章中,我们讨论了适用于一般DI的模式和原则,但是,除了几个例子之外,我们还没有详细研究如何使用任何特定的DI容器来应用它们。在本章中,你将看到这些总体模式如何映射到Autofac。你需要熟悉前几章的材料,才能从中充分受益。
Autofac是一个相当全面的DI容器,提供了一个精心设计和一致的API。它从2007年末开始存在,在撰写本文时,它是最受欢迎的容器之一。
在本章中,我们将研究如何利用Autofac来应用第1-3部分中提出的原则和模式。本章分为四个部分。你可以独立地阅读每一节,尽管第一节是其他章节的前提,第四节依赖于第三节中介绍的一些方法和类。
这一章应该能让你开始使用,以及处理你在日常使用Autofac时可能出现的最常见的问题。这并不是对Autofac的完整处理;这需要更多的章节,或者本身就是一本书。如果你想了解更多关于Autofac的信息,最好从Autofac的主页开始,网址是https://autofac.org。
13.1 介绍Autofac
在本节中,你将了解从哪里获得Autofac,你得到什么,以及如何开始使用它。我们还将看一下常见的配置选项。表13.1提供了你可能需要的基本信息,以便开始使用。
表13.1 Autofac概览
问题 | 答案 |
---|---|
我在哪里可以得到它? | 从Visual Studio,你可以通过NuGet获得它。包的名称是Autofac。另外,NuGet包也可以从GitHub仓库(https://github.com/autofac/Autofac/releases)下载。 |
哪些平台受到支持? | .NET 4.5(无.NET Core SDK)和.NET标准1.1(.NET Core 1.0、Mono 4.6、Xamarin.iOS 10.0、Xamarin.Mac 3.0、Xamarin.Android 7.0、UWP 10.0、Win-dows 8.0、Windows Phone 8.1)。支持.NET 2.0和Silver-light的较早版本可以通过NuGet历史记录获得。 |
它的费用是多少? | 没有。它是开源的 |
它是如何获得许可的? | MIT License. |
我在哪里可以得到帮助? | 你可以从与Autofac开发商有关的公司获得商业支持。请在https://autofac.readthedocs.io/en/latest/support.html 上阅读更多关于这些选择。除了商业支持,Autofac仍然是开源软件,有一个繁荣的生态系统,所以你也有可能(但不能保证)通过在Stack Overflow上发帖(https://*.com)或使用官方论坛(https://groups.google.com/group/autofac)获得帮助。 |
本章是基于哪个版本? | 4.9.0-beta1 |
-------- |
使用Autofac与使用我们将在下面几章讨论的其他DI容器没有什么不同。 如同Simple Injector和Microsoft.Extensions .DependencyInjection,使用是一个两步的过程,如图13.1所示。首先,你配置一个 ContainerBuilder,当你完成配置后,你用它来构建一个容器来解决组件。
图13.1 使用Autofac的模式是首先配置它,然后再解决组件。
当你看完这一节后,你应该对Autofac的整体使用模式有一个很好的感觉,你应该能够开始在良好的场景中使用它–所有的组件都遵循正确的DI模式,比如构造函数注入。让我们从最简单的场景开始,看看你如何使用Autofac容器来解析对象。
13.1.1 解决对象
任何DI容器的核心服务都是对对象图进行组合。 在这一节中,我们将看一下让你用Autofac组合对象图的API。
默认情况下,Autofac要求您在解决这些问题之前注册所有相关组件。然而,这种行为是可以配置的。下面的列表显示了Autofac的一个最简单的可能用途。
清单13.1 Autofac的最简单使用方法
var builder = new ContainerBuilder();
builder.RegisterType < SauceBéarnaise > ();
IContainer container = builder.Build();
ILifetimeScope scope = container.BeginLifetimeScope();
SauceBéarnaise sauce = scope.Resolve < SauceBéarnaise > ();
如图13.1所示,你需要一个ContainerBuilder实例来配置组件。在这里,你用builder注册具体的SauceBéarnaise类,这样当你要求它建立一个容器时,产生的容器就会被配置成SauceBéarnaise类。这又使你能够从容器中解析SauceBéarnaise类。
然而,在 Autofac 中,你永远不会从根容器本身解析,而是从生命周期范围解析。第 13.2.1 节详细介绍了生命周期范围,以及为什么从根容器解析是一件坏事。
警告 在Autofac中,直接从根容器中进行解析是一种不好的做法。这很容易导致内存泄漏或并发错误。相反,你应该总是从一个生命周期的范围来解析。
如果你没有注册SauceBéarnaise组件,试图解决它就会抛出一个ComponentNotRegisteredException,信息如下。
The requested service “Ploeh.Samples.MenuModel.SauceBéarnaise” has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
Autofac不仅可以用无参数构造函数解决具体的类型,还可以将一个类型与其他依赖自动连接起来。所有这些依赖都需要被注册。在大多数情况下,你会希望对接口进行编程,因为这引入了松耦合。为了支持这一点,Autofac允许你将抽象映射到具体类型。
将抽象概念映射到具体类型
而你的应用程序的根类型通常由它们的具体类型来解决,松耦合要求你将抽象映射到具体类型。基于这种映射创建实例是任何DI容器所提供的核心服务,但你仍然必须定义映射。在这个例子中,你将IIngredient接口映射到具体的SauceBéarnaise类,这使你能够成功地解析IIngredient。
var builder = new ContainerBuilder();
//将一个具体的类型映射到一个抽象中
builder.RegisterType < SauceBéarnaise > ().As < IIngredient > ();
IContainer container = builder.Build();
ILifetimeScope scope = container.BeginLifetimeScope();
//解决了SauceBéarnaise类
IIngredient sauce = scope.Resolve < IIngredient > ();
As方法允许一个具体类型被映射到一个特定的抽象。由于之前的As()调用,SauceBéarnaise现在可以被解析为IIngredient。
你使用ContainerBuilder实例来注册类型和定义映射。 RegisterType方法让你注册一个具体的类型。
正如你在清单13.1中看到的,如果你只想注册SauceBéarnaise类,你可以在这里停止。你也可以继续使用As方法来定义应该如何注册具体类型。
警告 与Simple Injector和Microsoft.Extensions.DependencyInjection相反,RegisterType和As方法定义的类型之间没有通用类型约束。这意味着有可能映射不兼容的类型。代码会被编译,但在运行时,当 ContainerBuilder 构建容器时,你会得到一个异常。
在许多情况下,通用的API就是你所需要的全部。 尽管它不像其他一些 DI 容器那样提供相同程度的类型安全,但它仍然是配置容器的一种可读的方式。 不过,在有些情况下,你需要一种更弱类型的方式来解决服务。有了Autofac,这也是可能的。
解决弱类型的服务
有时你不能使用通用API,因为你在设计时不知道适当的类型。你所拥有的只是一个Type实例,但你还是想获得该类型的实例。你在第7.3节看到了一个例子,我们在那里讨论了ASP.NET Core MVC的IControllerActivator类。相关的方法是这样的。
object Create(ControllerContext context);
如之前listing 7.8所示,ControllerContext捕获了控制器的类型,你可以使用ActionDescriptor属性的ControllerTypeInfo属性来提取它。
Type controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();
因为你只有一个Type实例,所以你不能使用通用的Resolve方法,而必须求助于弱类型的API。Autofac提供了一个弱类型的Resolve方法的重载,让你像这样实现Create方法。
Type controllerType = context.ActionDescriptor.ControllerTypeInfo.AsType();return scope.Resolve(controllerType);
Resolve的弱类型重载允许你直接将controllerType变量传递给Autofac。通常情况下,这意味着你必须将返回的值转换为某个抽象概念,因为弱类型的Resolve方法返回对象。然而,在IControllerActivator的情况下,这并不是必须的,因为ASP.NET Core MVC并不要求控制器实现任何接口或基类。
无论你使用哪种Resolve的重载,Autofac都能保证它能返回一个请求类型的实例,或者在有依赖不能满足的情况下抛出一个异常。 当所有需要的依赖都被正确配置后,Autofac可以自动连接所请求的类型。
在前面的例子中,范围是Autofac.ILifetimeScope的一个实例。为了能够解决所请求的类型,所有松耦合的依赖必须事先配置好。有很多方法可以配置Autofac,下一节将回顾最常见的方法。