公共服务注册表

本文关键字:注册表 公共服务 | 更新日期: 2023-09-27 17:58:20

很长一段时间以来,我们一直很幸运地拥有公共服务定位器(CSL)来解析来自未知来源的服务。然而,从一开始就没有任何与容器无关的解决方案来注册这些服务。我们一直面临的问题是,必须将组合代码耦合到特定的IoC容器,此外,还要致力于在整个应用程序中使用该容器。

我觉得我可能正在努力实现不可能的目标,但有人对如何实现公共服务注册(CSR)有什么想法吗?

我最初的想法是使用MEF来解析各种IContainerIntegrators(每个容器技术一个类),然后使用MEF解析各种IXXXContainerBindings(每个技术一个接口)。然后,用户可以围绕容器绑定开发应用程序,只需将绑定放入BIN目录即可实现插件架构。如果他们想使用一种新的容器技术,那么他们只需要开发一个新的IContainerIntegrator类和附带的IXXXContainerBinding,然后用它来编写自己的绑定。在应用程序启动时,CSR使用ServiceLocatorAggregator类将所有容器实例聚合到一个CSL中。

我已经完成了这项工作,但面临以下问题:

  • 任何调用当前(不完整)容器的绑定都是不稳定的,因为以前的注册可能会被后续绑定覆盖。这包括需要解析对象以做出注册决定的绑定(即"如果存在,则绑定此")
  • 可能存在两个绑定,它们公开相同的服务集。但是,我们希望"交错"这些注册。例如,"我希望服务A和C来自绑定X,服务B和D来自绑定Y"
  • 当自动连接所请求服务的依赖关系时,容器从自身解析服务。例如,'container.Bind<这>。到<那>()"将自动为实现注入从"容器"解析的服务,而不是从聚合服务定位器解析的服务

如果我完全没有抓住要点,请对我大喊大叫,但这不是依赖管理的最解耦的解决方案吗?这不是很好吗?我即将开始一个插件架构的大型企业项目。我不想提交到特定的IoC容器。

(附言:这个问题是关于支持发现的容器不可知组合的。请不要对SL和DI进行争论。SL在组合中使用,这就是为什么我在这里引用了这么多)。

公共服务注册表

您能实现的最解耦(也是最好)的解决方案是通过原则和模式而不是特定技术来实现松散耦合。

在整个应用程序中使用构造函数注入。确保任何应用程序层都不需要引用任何容器。然后在应用程序的根中组成整个应用程序图。

您不需要使用DI容器,但如果选择使用DI容器时,必须将其隔离到组合根。这意味着,如果您以后决定迁移到另一个容器,您只需要更改Composition Root。然而,穷人的DI也是一种选择。

您可以使用许多技巧来避免在大多数代码中依赖特定的IoC容器,其中最简单的是使用构造函数注入。如果您执意要使用ServiceLocator模式,只需创建自己的ServiceLocator类,该类封装了您计划使用的实际IoC容器内核。

也就是说,IoC容器的目的是实现"控制反转":即将控制从底层移动到顶层。这意味着您需要在应用程序的"顶部"(或"根")附近有一个点,它实际上知道它将要使用的所有服务实现,以及您的特定IoC实现。这应该仅限于少数几个类。通常,应用程序的"上下文根"是初始化IoC容器和服务定位器的地方。应该有一个特定的模块或一组模块负责设置所有绑定。

如果你想允许插件,你需要创建一个特定的API供它们使用和遵循。简单地允许其他包随意定义新的IoC绑定是一个灾难,因为你不知道这些不同的包在一起会有多好。

ASP.NET MVC 3就是一个很好的例子。它们具有特定的服务工厂定位器,您可以在Global Application_Start方法中覆盖这些定位器。为了实现其中一个工厂,您必须遵守他们为您提供的API。但是,您可以创建一个使用任何您想要的IoC容器的实现,或者根本不使用。您根本没有更改"绑定"。您只是告诉框架,对于当前应用程序,您希望它使用"此工厂"来创建控制器或建模元数据提供程序,而不是使用默认工厂。

要使用另一个更适用于特定示例的示例,让我们以ISearchProvider为例。你可能有一个内置的LuceneProvider,也许你的一个插件可以提供GoogleProvider。您希望使用以下哪种提供商?仅仅是GoogleProviderPlugin的存在就意味着LuceneProvider不再可用吗?搜索是否应该以某种方式结合这两个提供商的结果?用户是否应该能够从用户界面中选择一个或多个提供商?

不管这些问题的答案是什么,最终的要点是你希望你的应用程序来控制这一点,而不是插件。与其让插件全权处理DI绑定,不如告诉插件,"我允许你定义额外的搜索提供程序,下面是注册它们的方法。"它们可以通过多种方式注册,包括类注释/属性,或者只存在一个实现给定接口的类。但重要的一点是,有一个API专门定义了他们可以"插入"到什么,以及你对任何构建插件的人的要求。

现在,如果GoogleProvider具有在插件中定义的依赖项,则该插件可以根据需要解析这些依赖项。希望它能使用某种IoC容器,但如果没有,那就太麻烦了。对于他们使用的容器类型(如果有的话),您仍然可以是不可知论者。

如果您期望SearchProvider需要某些服务,则可以将这些服务或这些服务的工厂包括在内,作为插件初始化API的一部分。这样,您的插件就可以访问这些服务,而无需了解应用程序的IoC容器。