【问题标题】:Castle Windsor: Force resolver to use specified constructorCastle Windsor:强制解析器使用指定的构造函数
【发布时间】:2011-12-17 02:55:06
【问题描述】:

示例如下:

interface IComponentA {};

class ComponentA : IComponentA { };

interface IComponentB { };

class ComponentB : IComponentB { };

interface IComponentC { };

class ComponentC : IComponentC
{
    public ComponentC(IComponentA a)
    {
        Console.WriteLine("Constructor A"); 
    }

    public ComponentC(IComponentB b) 
    {
        Console.WriteLine("Constructor B");
    }
};

所有这些组件都注册在 Castle Windsor 容器中。

但是ComponentC 类有 2 个重载的构造函数。任何一个都可以在ComponentC被激活时使用。

我需要使用ComponentC(IComponentB b) 构造函数。

我暂时使用 UsingFactoryMethod() 方法来解决这个问题:

container
    .Register(Component
        .For<IComponentA>()
        .ImplementedBy<ComponentA>())
    .Register(Component
        .For<IComponentB>()
        .ImplementedBy<ComponentB>())
    .Register(Component
        .For<IComponentC>()
        .UsingFactoryMethod(() => new ComponentC(
            container.Resolve<IComponentB>())));

它有效,但温莎城堡可能提供了一些更好的方法来做到这一点?

非常感谢任何帮助。

谢谢。

【问题讨论】:

  • 如果它适合您的模型,您可以尝试使用 Typed Factory 工具。我相信它会自动选择与您传递给工厂方法的参数匹配的构造函数。这意味着您必须有一个工厂和一个该类型的实例才能传入。如果您最终保留现有代码,并且在同一个安装程序中注册两者,并且如果 ComponentB 没有依赖项,请考虑完全跳过 IComponentB/IComponentA 的注册。
  • This question 和一些 cmets(例如 ISubDependencyResolver)也可能有用。
  • 为什么使用哪个 .ctor 很重要?
  • @Krzysztof Koźmic构造函数ComponentC(IComponentA) 自己创建IComponentB,不要使用容器中的IComponentBComponentC(IComponentB) 自己创建IComponentA,不要使用容器中的IComponentA。因此,ComponentC 在这些情况下的行为是不同的。
  • 我想说你的班级可能有两个职责,也许你应该把它们分开?

标签: c# .net dependency-injection castle-windsor ioc-container


【解决方案1】:

Windsor 不为这种情况提供支持,因为它打破了它(以及大多数容器)运行基于的不成文假设之一:“所有构造函数都是平等的”。

这意味着,无论它选择哪个构造函数,组件的行为都应该没有功能差异。在所有条件相同的情况下,组件的依赖越多,其功能就越多,这就是 Windsor 倾向于首先选择更贪婪的构造函数的原因,但如果像你这样,我会说两种情况中的任何一种都发生了:

  • 您的组件实际上可能是两个伪装成一个的组件。在这种情况下,您可能需要拆分它。
  • 您的组件实际上确实使用它所拥有的两个依赖项进行操作,因此它应该有一个可以同时接受它们的构造函数。

我见过的另一种情况是这样的:

public class Foo
{
   public Foo(ISession session){/*code*/}
   public Foo(ISessionFactory factory):this(factory.OpenSession()){}
}

虽然乍一看这似乎是一个聪明的想法,但充其量它是多余的、令人困惑的和不必要的。如果您的案例看起来像这样,我将删除第二个构造函数。

【讨论】:

  • 很好的解释。我同意这一点。谢谢。
  • 您的第一个要点将我带到了 SOLID 范式的单一责任原则。确实非常令人印象深刻的答案!你的回答让我很开心,并解决了我的队友正在观察的神秘温莎城堡行为。我们正在开发一个 ASP.net MVC 项目,该项目具有带有重载构造函数的控制器,我很困惑 DI 容器如何总是调用具有最多参数的构造函数。
【解决方案2】:

嗯,这很糟糕,但有一种方法(过去我不得不对 Linq2Sql DataContext 对象进行此操作)。您创建一个装饰器类并注册它。

假设你有这个界面:

public interface IService 
{
    void DoSomething();
}

你有一个实现如下:

public class Service : IService
{
    private readonly ILogger _logger;

    public Service(ILogger logger)
        : this(logger, SomeDefaultListOfThings())
    {
    }

    // Let's say Windsor is calling this ctor for some reason (ArrayResolver?)
    public Service(ILogger logger, IEnumerable<object> emptyArrayFromWindsor)
    {
        _logger = logger;
        PutTheItemsSomewhere(emptyArrayFromWindsor);
    }

    public void DoSomething()
    {
        // Something that relies on the list of items...
    }
}

但正如示例所示,出于某种原因,温莎调用了错误的 ctor,否则您无法说服它(正如 Krzysztof 正确指出的那样)。然后你可以如下创建装饰器类,它只有一个构造函数,从而消除了歧义:

public class SpecificCtorServiceDecorator : IService
{
    private readonly IService _decorated;

    public SpecificCtorServiceDecorator(ILogger logger)
    {
        _decorated = new Service(logger);
    }

    public void DoSomething()
    {
        _decorated.DoSomething();
    }
}

然后您将注册该类:

container.Register(
    Component.For<IService>()
             .ImplementedBy<SpecificCtorServiceDecorator>());

当然,最好不要让这个奇怪的 default-values-in-other-constructors 事情发生(search for "Poor-man's Dependency Injection"),但在你无法控制你真正想要的类的情况下(比如我在 Linq2Sql 中,或者如果这将对 API 进行重大更改),那么这可能会让您摆脱麻烦。

【讨论】:

    猜你喜欢
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多