创建单例来访问统一容器或通过应用程序传递它是否更好?

btl*_*log 49 c# ioc-container unity-container

我正在使用IoC框架,我选择使用Unity.我还没有完全理解的一件事是如何更深入地解析应用程序中的对象.我怀疑我当时还没有灯泡可以说清楚.

因此,我尝试在psuedo'ish代码中执行以下操作

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

所以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);
    } 
}
Run Code Online (Sandbox Code Playgroud)

我可以看到三个选项:

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

[编辑]我不清楚的一件事是我想为foreach语句的每次迭代创建一个新的测试用例实例.上面的示例需要解析测试套件配置并填充测试用例对象的集合

Mar*_*ann 51

DI的正确方法是使用构造函数注入或其他DI模式(但最常见的是构造函数注入)将依赖项注入到使用者中,而不管DI Container如何.

在你的榜样,它看起来像你需要的依赖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;
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

注意如何组合readonly关键字和保护条款保护类的不变量,确保依赖提供给TestSuiteParser的任何成功创建实例.

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

public TestSuite Parse(XPathNavigator someXml) 
{ 
    List<XPathNavigator> aListOfNodes = DoSomeThingToGetNodes(someXml) 

    foreach (XPathNavigator blah in aListOfNodes) 
    { 
        this.testSuite.TestCase.Add(this.testCase); 
    }  
} 
Run Code Online (Sandbox Code Playgroud)

(但是,我怀疑可能涉及多个TestCase,在这种情况下,您可能需要注入抽象工厂而不是单个TestCase.)

从您的Composition Root,您可以配置Unity(或任何其他容器):

container.RegisterType<TestSuite, ConcreteTestSuite>();
container.RegisterType<TestCase, ConcreteTestCase>();
container.RegisterType<TestSuiteParser>();

var parser = container.Resolve<TestSuiteParser>();
Run Code Online (Sandbox Code Playgroud)

当容器解析TestSuiteParser时,它理解构造函数注入模式,因此它使用所有必需的依赖项自动连接实例.

创建一个Singleton容器或传递容器只是Service Locator反模式的两种变体,所以我不建议这样做.

  • 我说你不应该把容器放在周围或让它静止.当您需要从运行时值解析依赖项时,可以使用抽象工厂.参见例如http://stackoverflow.com/questions/1926826/cant-combine-factory-di/1927167#1927167 (3认同)
  • 我认为这个答案仍然没有回答原来的问题.在Web应用程序或基于请求的模型中,不需要传递容器,因为您可以将容器的实例(您将使用的唯一实例)填充到应用程序级字典中,例如`HttpApplicationState`. .但是,桌面应用程序中的顶级类(最接近UI需要编写的类)如何获得对容器的引用? (3认同)
  • 所以你说你不应该通过容器,你不应该让它静态.那时我没有选择.在许多情况下,我不能简单地注入依赖项,因为在用户输入我需要做的事情之前我不会知道.示例:我有打开不同类型窗口的按钮.当用户按下按钮AI时,不能说Container.Resolve <BaseWindows>("StringTiedToButtonA").那么我应该把它作为我注入的某种服务吗? (2认同)
  • 你能指出一些更好地解释这个问题的地方,比如展示容器互动.在您的示例中,您仍然需要访问即时出厂的容器.容器是如何到达那里的? (2认同)

Bru*_*ill 12

我是新手Dependency Injection,我也有这个问题.我正在努力思考DI,主要是因为我专注于将DI应用于我正在处理的一个类,并且一旦我将依赖项添加到构造函数中,我立即试图找到一些方法来获得统一容器到需要实例化此类的地方,以便我可以Resolve在类上调用该方法.因此,我一直在思考将统一容器全局化为静态或将其包装在单例类中.

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

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

而这段特别是"灯泡"时刻:

"99%的代码库应该不了解你的IoC容器.它只是使用容器的根类或引导程序,即使这样,只需要一个解析调用来构建依赖关系图并开始申请或要求."

本文帮助我理解我实际上不能访问应用程序中的统一容器,而只能访问应用程序的根目录.所以我必须反复应用DI原则一直回到应用程序的根类.

希望这能帮助那些和我一样迷茫的人!:)