【问题标题】:Dependency Injection with Interface implemented by multiple classes具有由多个类实现的接口的依赖注入
【发布时间】:2012-06-11 15:39:49
【问题描述】:

更新:有没有办法在 Windsor 以外的 IoC 框架中实现我想要做的事情? Windsor 会很好地处理控制器,但不会解决其他任何问题。我确定这是我的错,但我正在逐字遵循教程,并且对象没有通过 ctor 注入解析,尽管进行了注册和解析,它们仍然为空。我已经废弃了我的 DI 代码并暂时进行手动注入,因为该项目对时间很敏感。希望在截止日期前完成 DI。


我有一个解决方案,它有多个类都实现了相同的接口

举个简单的例子,Interface

public interface IMyInterface {
    string GetString();
    int GetInt();
   ...
}

具体的类

public class MyClassOne : IMyInterface {
    public string GetString() {
        ....
    }
    public int GetInt() {
        ....
    }
}

public class MyClassTwo : IMyInterface {
    public string GetString() {
        ....
    }
    public int GetInt() {
        ....
    }
}

现在这些类将在需要的地方注入到它们上面的层中,例如:

public class HomeController {

    private readonly IMyInterface myInterface;

    public HomeController() {}

    public HomeController(IMyInterface _myInterface) {
        myInterface = _myInterface
    }
    ...
}

public class OtherController {

    private readonly IMyInterface myInterface;

    public OtherController() {}

    public OtherController(IMyInterface _myInterface) {
        myInterface = _myInterface
    }
    ...
}

两个控制器都注入了相同的接口。

在我的 IoC 中使用适当的具体类解析这些接口时,我如何区分 HomeController 需要 MyClassOne 的实例和 OtherController 需要 MyClassTwo 的实例?

如何将两个不同的具体类绑定到 IoC 中的同一个接口?我不想创建 2 个不同的接口,因为这违反了 DRY 规则并且没有任何意义。

在温莎城堡中,我会有这样两行:

container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassOne>());
container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassTwo>());

这行不通,因为我只会得到MyClassTwo 的副本,因为它是最后一个为该接口注册的副本。

就像我说的,如果不为每个具体实例创建特定的接口,我不知道如何做到这一点,这样做不仅违反了 DRY 规则,而且也违反了基本的 OOP。我如何做到这一点?


根据 Mark Polsen 的回答更新


这是我当前的 IoC,.Resolve 语句会去哪里?我在 Windsor 文档中没有看到任何内容

public class Dependency : IDependency {

    private readonly WindsorContainer container = new WindsorContainer();

    private IDependency() {
    }

    public IDependency AddWeb() {
        ...

        container.Register(Component.For<IListItemRepository>().ImplementedBy<ProgramTypeRepository>().Named("ProgramTypeList"));
        container.Register(Component.For<IListItemRepository>().ImplementedBy<IndexTypeRepository>().Named("IndexTypeList"));

        return this;
    }

    public static IDependency Start() {
        return new IDependency();
    }
}

【问题讨论】:

  • 如有必要,您可以直接绑定到具体类。 (好吧,至少使用 Ninject 你可以,但我无法想象你不能用其他蘸酱做到这一点。)
  • 你不想那样做。你的类的依赖是不明确的。您不能只看其中一个类并告诉它需要特定的实现。我建议您在两个类中都使用工厂作为依赖项。在这里阅读更多:codeproject.com/Articles/386164/…
  • @jgauffin Windsor 的.Named(...) 方法的承诺是它可以解决这种歧义,但在实际使用中,我无法让Windsor 解决任何问题:-(
  • 你不明白我的意思。您无法通过查看类构造函数来判断是否需要特定的实现。这有点违反里氏替换原则。
  • @jgauffin 我完全理解。这就是 OOP 的全部思想,能够接受基础并提供更具体的实现。但无论哪种方式,就像我说的那样,我无法让 Windsor 解决任何问题,而不仅仅是这些有歧义的类。

标签: asp.net-mvc-3 oop dependency-injection dry


【解决方案1】:

希望你可以使用service overrides

例如

container.Register(
    Component.For<IMyService>()
        .ImplementedBy<MyServiceImpl>()
        .Named("myservice.default"),
    Component.For<IMyService>()
        .ImplementedBy<OtherServiceImpl>()
        .Named("myservice.alternative"),

    Component.For<ProductController>()
        .ServiceOverrides(ServiceOverride.ForKey("myService").Eq("myservice.alternative"))
);

public class ProductController
{
    // Will get a OtherServiceImpl for myService.
    // MyServiceImpl would be given without the service override.
    public ProductController(IMyService myService)
    {
    }
}

【讨论】:

  • 这看起来也很有希望!
  • 这个不太适合我,因为我的 IoC 不在 MVC UI 层中,所以我不能使用 Component.For&lt;ProductController&gt;(),因为我必须引用 UI 程序集,这将导致循环引用。
【解决方案2】:

您应该能够通过命名组件注册来完成它。

container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassOne>().Named("One"));
container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassTwo>().Named("Two"));

然后用

解决它们
kernel.Resolve<IMyInterface>("One");

kernel.Resolve<IMyInterface>("Two");

See: To specify a name for the component

【讨论】:

  • 我正在开发一个使用 Castle Windsor 的示例应用程序,它使用 container.Register(... 行,但在任何地方都没有使用任何 container.Resolve...container.Resolve... 是否有一个特定的地方,或者就在.Register 之后?
  • 对不起,不是容器,应该是内核,其中kernal是IKernel类型。
  • 我用我当前的 IoC 更新了我的问题,它与我的 MVC UI 位于一个单独的程序集中,并且它目前没有 Kernel 对象。这是从哪里来的?
  • 您应该能够从您的容器实例中调用 Resolve。它不一定来自 MicroKernel 实例。
  • 此方法不起作用 :-( 由于某种原因,Windsor 没有解决任何依赖项。我按照 Castle wiki 上的教程进行操作。ControllersInstaller 有效,但其余无法解决跨度>
【解决方案3】:

通常 DI 容器遵循注册、解析和释放模式。在注册阶段有两个步骤。第一个是按照您的操作指定映射。第二步是指定规则,这些规则控制在哪里注入。

当我们尝试使用装饰器解决横切关注点时,这个问题很常见。在这些情况下,您有多个类(装饰器)实现一个接口。

简而言之,我们需要实现 IModelInterceptorsSelector,它允许您编写命令式代码来决定将哪个 Interceptor 应用于哪些类型或成员。

这在 Mark Seemann 的 .Net 中的依赖注入一书中进行了详细描述。寻找第9章拦截或搜索上述接口。

我不是这方面的专家,但正在寻找完全相同的问题并在上面的书中找到了答案。

希望这会有所帮助。

问候 开发者1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-23
    • 2013-04-11
    • 2014-10-06
    • 1970-01-01
    • 2014-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多