DI with Unity Container - 使用附加参数解析的最佳实践?

DI with Unity Container - Best practices resolving with additional parameters?

示例:

IApple 和实施 AppleApple 的构造函数:

public Apple(IVitamin vitamin, int size)

我可以注册所有的DI和IApple:

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

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

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

好像把参数的字符串写在里面好麻烦("size")。当涉及其他参数时,这是进行 DI 的首选方法吗?还是我必须创建一个 AppleFactory(或通常是一个工厂)来处理它? (必须为每个具有非 DI 属性和 DI 属性的 class 编写一个工厂似乎有点过分了。

或者您不应该覆盖并手动设置 属性?

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

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

It seems bothersome that you have to write the string of the parameter in there ("size")

是的,它很丑陋,但没有办法解决它,至少在 Unity 中没有。

It seems overkill to have to write a factory for every class that has non-DI properties as well as DI ones

是的,但这可能是您无论如何都应该做的。您的大部分代码不应直接依赖于容器。

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

class AppleFactory : IAppleFactory
{
    ...

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

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

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

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

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

旁注:

在大多数情况下,当我需要注入原语时,我会使用 struct / class 像这样的东西

public struct AppleSize {
     public int Value { get; }
     .....
}

现在我可以在 container 中注册 AppleSize 的实例 Apple 构造函数将使用 AppleSize 而不是 int。避免注册原语。最好使用更具描述性的结构/class 定义。

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