创build一个单例来访问统一容器或将其传递给应用程序会更好吗?

我趾高气昂地使用IoC框架,并select使用Unity。 我仍然不能完全理解的一件事是如何更深入地parsing应用程序中的对象。 我怀疑我刚才没有弄清灯泡。

所以我想在伪代码中做如下的事情

void Workflow(IUnityContatiner contatiner, XPathNavigator someXml) { testSuiteParser = container.Resolve<ITestSuiteParser> TestSuite testSuite = testSuiteParser.Parse(SomeXml) // Do some mind blowing stuff here } 

所以testSuiteParser.Parse执行以下操作

 TestSuite Parse(XPathNavigator someXml) { TestStuite testSuite = ??? // I want to get this from my Unity Container List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { //EDIT I want to get this from my Unity Container TestCase testCase = new TestCase() testSuite.TestCase.Add(testCase); } } 

我可以看到三个选项:

  1. 创build一个Singleton来存储我可以在任何地方访问的统一容器。 我真的不是这种方法的粉丝。 添加这样的依赖来使用dependency injection框架似乎有点奇怪的一面。
  2. 将IUnityContainer传递给我的TestSuiteParser类以及它的每一个子类(假设它是n级深度,或者实际上深度为3级)。 在各处传递IUnityContainer只是看起来很奇怪。 我可能只需要克服这一点。
  3. 有正确的方式使用Unity的灯泡时刻。 希望有人能帮助轻弹开关。

[编辑]我不清楚的一件事是,我想为foreach语句的每个迭代创build一个testing用例的新实例。 上面的例子需要parsingtesting套件configuration并填充testing用例对象的集合

DI的正确方法是使用构造器注入或另一个DI模式(但是构造器注入是最常见的)将依赖关系注入到消费者中, 而不pipeDI容器如何

在你的例子中,它看起来像你需要TestSuiteTestCase依赖项,所以你的TestSuiteParser类应该通过它的(唯一的)构造函数来静态声明它需要这些依赖项。

 public class TestSuiteParser { private readonly TestSuite testSuite; private readonly TestCase testCase; public TestSuiteParser(TestSuite testSuite, TestCase testCase) { if(testSuite == null) { throw new ArgumentNullException(testSuite); } if(testCase == null) { throw new ArgumentNullException(testCase); } this.testSuite = testSuite; this.testCase = testCase; } // ... } 

请注意readonly关键字和Guard子句的组合如何保护类的不variables,确保依赖关系可用于任何成功创build的TestSuiteParser实例。

您现在可以像这样实现Parse方法:

 public TestSuite Parse(XPathNavigator someXml) { List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { this.testSuite.TestCase.Add(this.testCase); } } 

(但是,我怀疑可能涉及多个TestCase,在这种情况下,您可能需要注入一个Abstract Factory而不是一个TestCase。)

在您的Composition Root中 ,您可以configurationUnity(或任何其他容器):

 container.RegisterType<TestSuite, ConcreteTestSuite>(); container.RegisterType<TestCase, ConcreteTestCase>(); container.RegisterType<TestSuiteParser>(); var parser = container.Resolve<TestSuiteParser>(); 

当容器parsingTestSuiteParser时,它理解构造器注入模式,所以它自动连接实例及其所需的所有依赖项。

创build一个Singleton容器或传递容器只是服务定位器反模式的两个变体,所以我不会推荐这样做。

我是Dependency Injection新手,我也有这个问题。 我一直在努力想办法解决DI问题,主要是因为我正在专注于将DI应用到我正在使用的一个类中,并且一旦将这些依赖添加到构造函数中,我立即尝试find一些方法来获得统一容器到需要实例化这个类的地方,以便我可以调用类的Resolve方法。 因此,我正在考虑沿着将统一容器全局可用的方式作为静态或包装在单例类中。

我在这里阅读了答案,并没有真正理解被解释的内容。 最终帮助我“得到它”的是这篇文章:

http://www.devtrends.co.uk/blog/how-not-to-do-dependency-injection-the-static-or-singleton-container

而这一段尤其是“灯泡”时刻:

“99%的代码库不应该知道你的IoC容器,它只是使用容器的根类或者引导程序,即使这样,一个parsing调用就是构build你的依赖图所必需的,应用程序或请求“。

这篇文章帮助我了解,我实际上不能访问整个应用程序中的统一容器,而只能在应用程序的根本上访问统一容器。 所以我必须将DI原则反复应用到应用程序的根类。

希望这能帮助那些和我一样困惑的人! 🙂

你不应该真的需要在应用程序的很多地方直接使用你的容器。 你应该把所有的依赖关系放在构造函数中,而不是从你的方法中获取它们。 你的例子可能是这样的:

 public class TestSuiteParser : ITestSuiteParser { private TestSuite testSuite; public TestSuitParser(TestSuit testSuite) { this.testSuite = testSuite; } TestSuite Parse(XPathNavigator someXml) { List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) foreach (XPathNavigator blah in aListOfNodes) { //I don't understand what you are trying to do here? TestCase testCase = ??? // I want to get this from my Unity Container testSuite.TestCase.Add(testCase); } } } 

然后在整个应用程序中以相同的方式执行。 你当然会在某个时候解决某些问题。 以asp.net mvc为例,这个地方在控制器工厂。 那是创build控制器的工厂。 在这个工厂中,你将使用你的容器来parsing你的控制器的参数。 但是这只是整个应用程序中的一个地方(当你做更高级的东西的时候,可能还会有更多的地方)。

还有一个很好的项目叫做CommonServiceLocator 。 这是一个具有所有stream行的ioc容器的共享接口的项目,所以你不需要依赖于特定的容器。

如果只有一个服务构造函数可以传递“ServiceLocator”,但是设法“声明”它所要注入的类的依赖关系(即不隐藏依赖关系),那么所有的(? )对服务定位器模式的反对可以被搁置。

 public class MyBusinessClass { public MyBusinessClass(IServiceResolver<Dependency1, Dependency2, Dependency3> locator) { //keep the resolver for later use } } 

可悲的是,上述显然将只存在于我的梦想,因为c#禁止variablesgenerics参数(仍然),所以手动添加一个新的通用接口,每次需要一个额外的generics参数,将是笨拙的。

另一方面,如果可以通过以下方式实现c#的限制,则可以达到上述目的。

 public class MyBusinessClass { public MyBusinessClass(IServiceResolver<TArg<Dependency1, TArg<Dependency2, TArg<Dependency3>>> locator) { //keep the resolver for later use } } 

这样,只需要做额外的打字就可以达到同样的效果。 我不确定的是,如果考虑到TArg类的正确devise(我假设聪明的inheritance将被用来允许无限嵌套TArg通用参数),DI容器将能够正确地parsingIServiceResolver 。 这个想法最终是简单地传递IServiceResolver相同的实现,不pipe被注入的类的构造函数中的generics声明是什么。