项目
版本

如何使 Autofac 与应用程序隔离?

有时,您可能希望尽量减少对 Autofac 库和 IoC 容器的引用。根据您的应用程序结构,可能获得不同程度的成功。这里有一些关于如何组织应用程序以最小化 Autofac 引用的提示,以及如果您选择这样做,需要注意的一些事项。

没有一个方法能解决所有问题。 您需要根据应用程序的结构和目标进行混合和匹配。

应用程序启动

应用程序启动时,会构建 IoC 容器并进行注册。这也是 IoC 容器与应用程序的 组合根 集成的地方。对于控制台应用,这是 Main 方法;对于 ASP.NET 应用,这是 Startup 类或 Global.asax;对于其他应用,有其他入口点。

不应试图将 IoC 容器与应用程序的这一部分分离。这是它具体连接到事物的地方。如果您尝试从包含以下代码的模块(如 Global.asaxStartup 类)中移除 Autofac 包/库引用,节省一下时间,别这么做。您可能会花很多时间编写抽象和包装,但只会复制大量特定于 Autofac 的语法和功能。

组件注册

Autofac 与应用程序连接的大部分地方是您将组件注册到容器的地方。有几种方法可以限制这里与 Autofac 的连接。

模块扫描

Autofac 支持通过 模块扫描 注册内容。如果您的应用程序有一个已知的插件集或多个可执行文件,并且想要注册实现特定接口或遵循特定命名规则的所有类型,这很有帮助。

然而,在使用模块扫描时,很难配置单个组件。例如,你不能真正地说:“注册所有这些类型,但是只有这少数几个需要单例,而其余的必须按依赖关系实例化。”

如果您发现想使用模块扫描,但需要这种级别的控制,您可能需要使用 .NET 反射并在自己的代码中处理额外功能。例如,如果您想通过自定义属性指定生命周期范围,您可以创建该自定义属性,并相应地编写一组方法来遍历一组模块,根据属性值进行注册。

模块

使用 Autofac 模块 是将相关类型注册分组的好方法,但它将实现 Autofac.Module 接口的类型与 Autofac 库绑定在一起。

解决这个问题的一种方法是将 Autofac 模块放入单独的库中。这样,对于特定功能,您将有两个库:

  • 产品库:此库包含实际代码,不引用 Autofac。
  • 模块库:此库引用产品库以及 Autofac。这就是您放置 Autofac 模块的地方。

然后,您的应用程序可以使用模块扫描来查找所有“模块库”,并在其中注册模块。(一种简化扫描的方法是采用模块库的命名约定,然后只在这些库上运行扫描。)

配置

Autofac 配置 系统完全基于配置文件工作,并允许您在应用程序启动代码中指定无需引用其他位置的注册项。它不如 Autofac 模块 功能全面,因此您可能无法全部使用这种方式注册,但它对少量注册很有帮助。

您也可以使用配置系统注册 Autofac 模块——这可以让您在 “模块库”(见上文)中指定模块,跳过模块扫描。

IStartable

Autofac 提供了 IStartable 接口,您可以使用它来自动 解析一个组件并执行代码

如果您的 IStartable 注册为单例(通常应该是),您可以利用 AutoActivate() 方法以及 OnActivated 处理器来替换它并移除对 Autofac 的依赖:

var builder = new ContainerBuilder();
builder
    .RegisterType<TypeRequiringWarmStart>()
    .AutoActivate()
    .OnActivating(e => e.Instance.Start());

服务解析

在大多数 DI/IoC 使用情况下,您不应该引用 IoC 容器——相反,您会有构造参数和/或可以通过容器设置的属性。

然而,您可能会发现自己在以下几个领域与 Autofac 相关联...

服务定位

一些框架缺乏在应用程序启动级别启用所有依赖注入钩子的组合根挂钩。例如,经典的 ASP.NET HttpModules 就是这种情况——通常没有允许您向模块注入依赖项的钩子。在这种情况下,您可能会发现使用服务定位很有用(尽管在可能的情况下应尽量减少服务定位,因为它是反模式 参见)。

在需要服务定位但又不想绑定到 Autofac 的情况下,请考虑使用像 CommonServiceLocatorMicrosoft.Extensions.DependencyInjection 这样的抽象。在 ASP.NET MVC 应用程序中,您已经提供了用于服务定位的 DependencyResolver;其他应用程序类型可能已经提供了类似抽象。通过使用其中一个抽象,您可以移除对 Autofac 的引用……尽管您仍需要引用抽象。

隐式关系

Autofac 支持多种 隐式关系 ,如 IEnumerable<T>Lazy<T>Func<T>。大部分关系基于核心 .NET 类型。然而,如果您使用以下内容,您将绑定到 Autofac:

  • IIndex<X, B>(索引集合)
  • Meta<T>Meta<B, X>(强类型元数据)

对象的实例名称/键或元数据没有替代品或解决方案。如果您需要这种功能,您只能使用这些关系。

但是,您可以通过代码来潜在地减少对这些关系的使用:

  • 创建工厂:您可以在工厂中包装对这些关系的使用。在应用程序代码库中定义工厂接口,并在允许引用 Autofac 的单独库中定义实现。
  • 使用 lambda 注册:您可以使用 lambda 注册组件,并在 lambda 中直接解析值。这有点像将工厂放在 lambda 注册中,而不是为其定义单独的接口。这会在应用程序代码中添加一点,将其放入注册(例如,使用元数据和服务键),但这可能是可接受的妥协。
在本文档中