带Unity容器的DI - 使用其他参数解决的最佳做法?

时间:2017-12-19 14:44:59

标签: c# .net unity-container

示例:

IApple和实施AppleApple的构造函数:

public Apple(IVitamin vitamin, int size)

我可以注册所有DI和IApple:

container.RegisterType<IApple,Apple>();
container.RegisterInstance<IVitamin>(vitamin);

我现在可以在创建apple实例时覆盖参数以插入int size参数:

var apple = container.Resolve<IApple>(new ParameterOverrides<Apple> {{"size", 9001}}

你必须在那里写参数的字符串(&#34; size&#34;)似乎很麻烦。当涉及其他参数时,这是否是首选DI?或者我是否必须创建一个处理该工作的AppleFactory(或者通常是工厂)? (对于每个具有非DI属性以及DI属性的类,必须编写工厂似乎有点过分。

或者你不应该手动覆盖和设置属性吗?

var apple = container.Resolve<IApple>();
apple.Size = 9001;

这样代码逻辑就会从构造函数转移到属性的setter。

3 个答案:

答案 0 :(得分:3)

  

你必须在那里写参数的字符串似乎很麻烦(&#34; size&#34;)

是的,它很难看,但是没有办法绕过它,至少不是Unity。

  

为每个具有非DI属性以及DI属性的类编写工厂似乎有点过分

是的,但它可能是你应该做的。您的大多数代码不应直接依赖于容器。

如果你是一个工厂,参数覆盖并不是那么难看,因为你可以使用nameof而不是字符串:

class AppleFactory : IAppleFactory
{
    ...

    public IApple CreateApple(int size)
    {
        return _container.Resolve<IApple>(new ParameterOverrides<Apple> {{nameof(size), size}};
    }
}

另一种方法是使用我的Unity.Extras.AutoFactory扩展程序,但请记住它处于alpha状态...

答案 1 :(得分:1)

我建议你想为什么要以这种方式向课堂注入integer。如果使用unity实例化的每个Apple都注入了相同的值,那么该值是否为常数?如果是的话,你真的不需要注射它。它可以只是Apple的一部分。但是在您的示例中,如果Apple表示实体或具有状态且可以具有不同大小的对象,Factory肯定似乎是更好的选择。 Factory将在初始化期间设置属性值。如果您正在测试代码,那么您将自己帮忙将这些决策转移到Factory并保持IoC容器配置专注于构建依赖树而不是处理初始状态对象的值。

答案 2 :(得分:1)

我认为你的例子太简单了。如果您要创建不同大小的苹果,您可能不会从容器中解析它们(通过容器创建苹果的方式有点奇怪,它看起来更适合{{ 1}}然后factory)。

不要忘记容器的主要目的是连接复杂的对象组合/依赖项,这些组件/依赖项应该能够从容器的当前状态创建。当你发现自己为Resolve方法提供参数时,我认为你应该停下来并试图避免它,可能是一些重新设计,重构是正确的方向。

旁注

在大多数情况下,当我需要注入原语时,我会使用像这样的结构/类

container

现在我可以在public struct AppleSize { public int Value { get; } ..... } 中注册AppleSize的实例 Apple构造函数将使用container而不是AppleSize。避免注册原语。更好地使用更具描述性的结构/类定义。

如果场景更强复杂(要注入的原语可能依赖于太多的参数,需要计算/操作)那么我会创建int封装任何与创建对象相关的操作,这与我认为的场景更接近。