如何在Ninject中将同一依赖项绑定到多个依赖项?

4
让我们假设我有三个接口:IFoo、IBar和IBaz。我还有类Foo、Bar和Baz,它们分别是这些接口的实现。
在这些实现中,每个类都依赖于接口IContainer。因此,对于Foo(以及类似地对于Bar和Baz),实现可能会如下所示:
class Foo : IFoo
{
    private readonly IDependency Dependency;

    public Foo(IDependency dependency)
    {
           Dependency = dependency;
    }

    public void Execute()
    {
         Console.WriteLine("I'm using {0}", Dependency.Name);
    }
}

假设我有一个名为Container的类,其中包含了IFooIBarIBaz 的实例:

class Container : IContainer
{
    private readonly IFoo _Foo;
    private readonly IBar _Bar;
    private readonly IBaz _Baz;

    public Container(IFoo foo, IBar bar, IBaz baz)
    {
        _Foo = foo;
        _Bar = bar;
        _Baz = baz;
    }
}

在这种情况下,我希望实现类Container绑定到IContainer,并约束注入到IFooIBarIBaz中的IDependency对于所有三个接口都是相同的。手动实现可能会如下所示:
IDependency dependency = new Dependency();
IFoo foo = new Foo(dependency);
IBar bar = new Bar(dependency);
IBaz baz = new Baz(dependency);
IContainer container = new Container(foo, bar, baz);

我该如何在Ninject中实现这个功能?
注意:我不是在询问如何处理嵌套依赖项。我的问题是如何确保给定的依赖项在一个材料化服务的对象集合中是相同的。
非常明确地说,我知道Ninject在其标准形式下将生成等效于以下代码的代码:
IContainer container = new Container(new Foo(new Dependency()), new Bar(new Dependency()), new Baz(new Dependency()));

我不希望出现那种行为。而且我也不能将 Dependency 创建为单例。具体来说,这意味着如果我有多个请求 GetService<IContainer>,那么 Ninject 调用应该与以下手动注入语句在语义上等价:

var dep1 = new Dependency();
var container1 = new IContainer(new Foo(dep1), new Bar(dep1), new Baz(dep1));

var dep2 = new Dependency();
var container2 = new IContainer(new Foo(dep2), new Bar(dep2), new Baz(dep2));

1
这应该很简单。看看你如何绑定事物。希望这可以帮到你。http://stefanoricciardi.com/2011/01/21/ninject-mini-tutorial-part-1/ - DarthVader
@DarthVader:请查看我在Konstantin答案下留的评论。 - Mike Bailey
你不需要“穷人版”的依赖注入,这就是你上一段代码所做的。简单来说,对于Foo、Bar和Baz,你将获得相同的依赖实例。要更好地理解这些概念,请查看依赖注入的生命周期和作用域,例如:每个请求、每个线程、单例等。 - DarthVader
我认为你不需要任何ToConstant代码或任何复杂的东西。也许,对你来说最好的做法是测试、调试它。使用基本绑定通常是每个请求的实例,你会没问题的。 - DarthVader
3个回答

3

使用ToConstant方法指定要绑定的确切实例。如果有机会,可以使用Unbind重新绑定到另一个实例:

        IKernel nKernel = new StandardKernel();

        nKernel.Bind<IFoo>().To<Foo>();
        nKernel.Bind<IBar>().To<Bar>();
        nKernel.Bind<IBaz>().To<Baz>();
        nKernel.Bind<IContainer>().To<Container>();

        nKernel.Bind<IDependency>().ToConstant(new Dependency());            

        Container c = nKernel.Get<Container>();
        //utilize the container...

        nKernel.Unbind<IDependency>();
        nKernel.Bind<IDependency>().ToConstant(new Dependency());

        c = nKernel.Get<Container>();
        //utilize the container...

这是否能保证在IContainerIFooIBarIBaz所生成的Dependency完全相同?那是我的主要关注点。您并未回答这个问题。 - Mike Bailey
@MikeBantegui 您的 Foo、Bar、Baz 将会得到三个不同类型的 Dependency 对象。 - horgh
我不想要那个。正如我的问题所述并且我的编辑反映的那样,我想知道是否有可能将相同的“Dependency”绑定到容器中的“Foo”、“Bar”和“Baz”依赖项。 - Mike Bailey
@MikeBantegui 请查看我的编辑...但假设对象在绑定时存在。 - horgh
@MikeBantegui,ToConstant有用吗?还是我还漏了什么? - horgh
我需要进行测试,很快会向您报告我的发现。 - Mike Bailey

1

编辑:

这个怎么样?

kernel.Bind<IContainer>().ToMethod(context =>
            {
                IDependency dependency = new Dep();
                IFoo foo = new Foo(dependency);
                IBar bar = new Bar(dependency);
                IBaz baz = new Baz(dependency);
                return new Container(foo, bar, baz);
            });

有没有什么方法可以避免使用 InSingletonScope?我正在处理的对象在作为单例时表现极差。 - Mike Bailey
@MikeBantegui 我也这么认为,我已经更新了我的答案并提出了一个想法。 - armen.shimoon
这在技术上不还是一个单例吗?似乎适当的做法是将调用设置为:.ToMeth(context => new Dep()) - Mike Bailey
@MikeBantegui 那么你将无法获得每个Foo、Bar和Baz的相同实例... - armen.shimoon
问题是,我需要它对于Container中的每个FooBarBaz都相同,但对于许多Container来说却是不同的。 - Mike Bailey
显示剩余4条评论

1

这是另一次尝试:

public class Container : IContainer
{
    private IFoo _foo;
    private IBar _bar;
    private IBaz _baz;


    public Container(IContainerDependencies dependencies)
    {
        _foo = dependencies.Foo;
        _bar = dependencies.Bar;
        _baz = dependencies.Baz;
    }
}

public class ContainerDependencies : IContainerDependencies
{
    public ContainerDependencies(IFoo foo, IBar bar, IBaz baz)
    {
        Foo = foo;
        Bar = bar;
        Baz = baz;
    }

    public IFoo Foo { get; set; }
    public IBar Bar { get; set; }
    public IBaz Baz { get; set; }
}

public interface IContainerDependencies
{
    IFoo Foo { get; set; }
    IBar Bar { get; set; }
    IBaz Baz { get; set; }
}

然后:

var kernel = new StandardKernel();
kernel.Bind<IFoo>().To<Foo>();
kernel.Bind<IBar>().To<Bar>();
kernel.Bind<IBaz>().To<Baz>();
kernel.Bind<IContainerDependencies>().ToMethod(context =>
    {
        context.Kernel.Unbind<IDependency>();
        context.Kernel.Bind<IDependency>().ToConstant(new Dep());
        return context.Kernel.Get<ContainerDependencies>();
    });
 kernel.Bind<IContainer>().To<Container>();

在创建容器的第二个实例时,您可能会遇到以下异常:激活IDependency时出错:存在多个匹配绑定。 - horgh
这实际上会破坏依赖于“IDependency”的其他绑定。更好的方法是删除所有当前绑定,创建新的绑定,解析实例,然后再添加回先前的绑定。虽然这似乎有点不太理想。 - Mike Bailey
有时候,最简单的解决方案就是硬编码你需要的内容,不必担心为你的一次性情况提供“通用”或“动态”的解决方案。我不确定这里的收益是否值得在这个时候去做。 - armen.shimoon

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