将单例绑定到多个服务中的Ninject

9
我有一个问题,它似乎与http://markmail.org/message/6rlrzkgyx3pspmnf中描述的问题非常相似,这个问题是关于单例实际上在使用不同的服务类型访问时创建多个实例。
我正在使用Compact Framework的最新版本Ninject 2,我的确切问题是,如果我将相同的提供程序方法绑定到:
Func<Service> serviceCreator = () => new Service(false);
kernel.Bind<IService>().ToMethod(serviceCreator).InSingletonScope();
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope();

如果我将IService和Service都解决,它似乎会创建2个Service实例。

这会在解决Service时导致循环依赖异常。

这是设计如此还是一个bug?


顺便说一句,我相信在Ninject的2.3和2.4版本中有一些不一致性正在被清理掉,以确保以这种方式重用的内容只会被激活和/或清理一次。 - Ruben Bartelink
请参见 V3 版本的回答:https://dev59.com/Cmkv5IYBdhLWcg3w_lzE - Ruben Bartelink
相关:http://stackoverflow.com/questions/8303661/ninject-binding-interface-to-interface/8303826#comment16639462_8303826 - Ruben Bartelink
3个回答

11

在V3版本中,有一种解决方法,即通过Bind上新增的重载,请参见相关问题


如果要共享单例,需要将第二个 Bind 改为:

kernel.Bind<Service>().ToMethod(()=>kernel.Get<IService>()).InSingletonScope();

关于循环引用和混淆等问题,隐式的 self-binding 会为 Service 添加一个隐式绑定注册。您应该发布异常。

编辑:关于您的评论。如果您这样做:

Func<Service> serviceCreator = () => new Service(false);
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope();
kernel.Bind<IService>().ToMethod(()=>kernel.Get<Service>()).InSingletonScope();

IService被解析时,不会生成隐式类自绑定 - 它将使用现有的绑定。

最近有人在Stack Overflow上提出了一个与此类似的问题,但遇到了IInitializable的问题 - 该例子具有正确的顺序,但以上示例基于我阅读源代码和生成隐式类自绑定的方式是有意义的。


这实际上在我的情况下会导致堆栈溢出。等我有时间了,我会尝试分离问题并发布我情况的最简示例。 - Michał Drozdowicz
嘿,看来我把你的第一个建议用错了 - 我使用了 Get<Service> 而不是 Get<IService>。唉。;) - Michał Drozdowicz
顺便提一下,如果您在单例的多个绑定中发现任何奇怪的问题,请阅读我刚刚在问题上添加的评论 - Ninject 的处理方式将有所改进。 - Ruben Bartelink

6

顺便提一下,Ninject 3允许使用这种语法

kernel.Bind<IService, Service>().ToMethod(serviceCreator).InSingletonScope();

或者,同样的:
kernel.Bind(typeof(IService), typeof(Service)).ToMethod(serviceCreator).InSingletonScope();

如果你有很多服务,或者在运行时动态发现服务(你可以直接将params风格的参数作为数组传递),那么这种后一种方法会更好。


2
这是正确的方法,但可能在最初提问时不可用。 - BatteryBackupUnit
@BatteryBackupUnit:关于你的编辑:那看起来不像是有效的C#语法。你能澄清一下吗,或者可能将其作为你自己的答案添加吗? - StriplingWarrior
2
抱歉,我可能有点急切。对于Bind,有一个重载Bind(params Type[] services),它接受(几乎)任何数量的类型。 Bind<IX, IFoo>最多支持4种类型。因此,当您有超过4种类型或使用反射获取类型时,另一个重载会很有用。你可以用更好的措辞将这个问题加入到你的答案中吗?这会让答案完整IMHO。 - BatteryBackupUnit

4
我们在项目中使用了Ruben的方法,但发现在绑定时为什么要返回到内核不是很直观。我创建了一个扩展方法和帮助类(如下),这样你就可以这样做:
kernel.Bind<IService>().ToExisting().Singleton<Service>();

对我来说,那似乎更清楚地表达了意图。

public static class DIExtensions
{
    public static ToExistingSingletonSyntax<T> ToExisting<T>(this IBindingToSyntax<T> binding)
    {
        return new ToExistingSingletonSyntax<T>(binding);
    }
}

// Had to create this intermediate class because we have two type parameters -- the interface and the implementation,
// but we want the compiler to infer the interface type and we supply the implementation type.  C# can't do that.
public class ToExistingSingletonSyntax<T>
{
    internal ToExistingSingletonSyntax(IBindingToSyntax<T> binding)
    {
        _binding = binding;
    }

    public IBindingNamedWithOrOnSyntax<T> Singleton<TImplementation>() where TImplementation : T
    {
        return _binding.ToMethod(ctx => ctx.Kernel.Get<TImplementation>()).InSingletonScope();
    }


    private IBindingToSyntax<T> _binding;
}

不错的例子。顺便说一下,我相信有一个新的Bind<>.ToBinding或类似的东西内置在某个扩展中/在@Remo Gloor的博客文章中,它将您的机制产品化了。 - Ruben Bartelink
@RubenBartelink 是正确的:https://github.com/ninject/ninject.extensions.contextpreservation - Filip Cornelissen
我之前评论中提到的帖子链接是:http://www.planetgeek.ch/2011/12/30/new-features-and-changes-of-ninject-3-0/。 - Ruben Bartelink

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