使用由多个类实现的接口的依赖注入

9
更新:除了Windsor,有没有其他的IoC框架可以实现我要做的事情?Windsor可以很好地处理控制器,但无法解析其他任何内容。虽然我按照教程逐字逐句地操作,但对象未能通过ctor注入进行解析,它们仍为空。我已经放弃了DI代码,并手动注入以满足项目的时间敏感性。希望在截止日期之前能够解决DI问题。
我有一个解决方案,其中多个类都实现相同的接口。
作为一个简单的例子,是"Interface"。
public interface IMyInterface {
    string GetString();
    int GetInt();
   ...
}

具体类(The concrete classes)
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规则,也没有意义。
在Castle Windsor中,我会有两行代码像这样:
container.Register(Component.For<IMyInterface>().ImplementedBy<MyClassOne>());
container.Register(Component.For<IMyInterface>().ImplementedBy<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可以这样做,但我想象不出您不能使用其他依赖项注入框架也能实现。) - Buildstarted
你不想这样做。你的类的依赖关系是不明确的。你不能只看一个类并告诉它需要一个特定的实现。我建议你在两个类中都使用工厂作为依赖项。在这里阅读更多信息:http://www.codeproject.com/Articles/386164/Get-injected-into-the-world-of-inverted-dependenci - jgauffin
@jgauffin Windsor的.Named(...)方法的承诺是可以解决这种歧义,但实际使用中,我无法让Windsor解决任何问题 :-( - CD Smith
你不理解我的意思。仅凭类构造函数的外观,你无法确定需要特定的实现方式。这有点违反了里氏替换原则。 - jgauffin
@jgauffin 我完全理解。这就是面向对象编程的整个思想,能够接受一个基础并提供更具体的实现。但无论如何,就像我说的那样,我无法让Windsor解决任何问题,不仅仅是这些存在歧义的类。 - CD Smith
3个回答

7

我希望您能使用服务覆盖

例如:

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)
    {
    }
}

这看起来也很有前途! - CD Smith
这个对我来说不太适用,因为我的 IoC 不在 MVC UI 层中,所以我不能使用 Component.For<ProductController>(),因为我必须引用 UI 程序集,这会导致循环引用问题。 - CD Smith

5
您应该能够通过命名组件注册来实现它。
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");

查看:指定组件名称


我正在使用一个使用Castle Windsor的示例应用程序,它使用container.Register(...行,但它没有在任何地方使用container.Resolve...container.Resolve...有特定的位置还是就在.Register之后? - CD Smith
抱歉,应该是内核而不是容器,其中内核是IKernel类型。 - Mark Polsen
我已更新我的问题并附上了我当前的IoC容器,它位于与我的MVC用户界面不同的程序集中,并且目前没有Kernel对象..那是从哪来的? - CD Smith
你应该能够从你的容器实例中调用Resolve方法。它不一定要来自MicroKernel实例。 - Mark Polsen
这个方法不起作用 :-( Windsor 由于某些原因无法解决任何依赖项。我按照 Castle Wiki 上的教程操作。ControllersInstaller 起作用,但其余部分无法解决。 - CD Smith

2
通常,DI容器遵循注册、解析和释放模式。在注册阶段有两个步骤。第一步是像你所做的那样指定映射。第二步是指定规则,这些规则控制注入到哪里。
当我们使用装饰器来解决横切关注点时,这个问题非常普遍。在这些情况下,您有多个实现单个接口的类(装饰器)。
简而言之,我们需要实现IModelInterceptorsSelector,它允许您编写命令式代码,决定将哪个拦截器应用于哪些类型或成员。
这在Mark Seemann的《.Net依赖注入》一书中有详细描述。查找第9章拦截或搜索上述接口。
我不是这方面的专家,但正在寻找完全相同的问题,并在上述书中找到了答案。
希望这能帮助到您。
敬礼 Dev1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接