共同服务登记处

时间:2011-06-04 17:50:30

标签: c# .net dependency-injection inversion-of-control ioc-container

很长一段时间以来,我们很幸运能够使用公共服务定位器(CSL)来解决来自未知来源的服务。但是,从来没有任何与容器无关的解决方案首先注册这些服务。我们一直面临着必须将组合代码耦合到特定IoC容器的问题,而且,我们承诺在整个应用程序中使用该容器。

我觉得我可能会尝试在这里实现不可能,但有没有人对如何实现公共服务注册表(CSR)有任何想法?

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

我有这个工作,但面临以下问题:

  • 任何调用当前(不完整)容器的绑定都是不稳定的,因为之前的注册可能被后续绑定覆盖。这包括需要解析对象以进行注册决策的绑定(即“如果存在则将其绑定”)。
  • 可能存在两个绑定,它们暴露同一组服务。但是,我们希望“交织”这些注册。例如。 '我希望服务A和C来自绑定X,服务B和D来自绑定Y'。
  • 容器在自动连接所请求服务的依赖关系时自行解析服务。例如。 'container.Bind< This> .To< That>()'将自动注入来自'container'解析的服务的实现 - 而不是来自聚合服务定位器。

如果我完全忽略了这一点,请大声喊叫,但这不是最依赖解耦的依赖关系管理解决方案吗?不是很好吗?我即将开始使用插件架构开展大型企业项目。我不想提交特定的IoC容器。

(ps这个问题是关于支持发现的与容器无关的组合。请不要就SL与DI进行辩论。在组合中使用SL,这就是我在这里引用它的原因。)

2 个答案:

答案 0 :(得分:9)

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

在整个应用程序中使用构造函数注入。确保您的应用程序层都不需要引用任何容器。然后是compose the entire application graph in the root of the application

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

答案 1 :(得分:1)

您可以使用许多技巧来避免在大多数代码中依赖特定的IoC容器,最简单的方法是使用构造函数注入。如果您使用服务定位器模式已经死定,只需创建自己的Service Locator类,它将包装您计划使用的实际IoC容器内核。

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

如果您想允许插件,则需要创建一个特定的API供他们使用和遵守。简单地允许其他软件包定义新的IoC绑定是一种灾难,因为你不知道这些不同的软件包将在多大程度上一起运行。

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

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

无论这些问题的答案如何,最终的一点是您希望应用程序控制它,而不是插件。您想告诉插件“我允许您定义其他搜索提供程序,以及如何注册它们”,而不是让插件 carte blanche 与您的DI绑定相混淆。它们可以通过各种方式进行注册,包括类注释/属性或仅仅存在实现给定接口的类。但重要的是,有一个API专门定义了他们可以“插入”的内容,以及构建插件的任何人所需的内容。

现在,如果GoogleProvider具有在插件中定义的依赖项,那么该插件可以解析它们想要的那些依赖项。希望它会使用某种IoC容器,但如果没有,那就没有皮肤了。如果有的话,你仍然可以不知道他们使用的容器类型。

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