如何设置一个 Ninject 单例实例

17

我有一个类(MyFacade),我使用Ninject注入了参数:

我有一个类(MyFacade),我使用Ninject注入了参数:

class MyFacade
{
    IDemoInterface demo;

    public MyFacade(IDemoInterface demo)
    {
        this.demo = demo;
    }

    public void MyMethod()
    {
        Console.WriteLine(demo.GetInfo());
    }
} 

当然,我必须设置Ninject以注入我的参数(IDemoInterface)的适当实现

我知道,我可以通过执行kernel.Get<MyFacade>();来实例化MyFacade对象,而不需要设置其他任何内容。目前,我的外观没有接口(因为它是我唯一的实现,也许我将添加其接口以用于标准目的)

如果我想使这个外观成为单例,我知道两种方法:创建一个空构造函数并通过执行kernel.Get<IDemoInterface>();来传递参数,或者像这样设置Ninject:kernel.Bind<MyFacade>().To<MyFacade>().InSingletonScope();

第二种方法似乎是更好的方法,但您知道其他设置为单例的方式吗?


我也可以执行 kernel.Bind<MyFacade>().ToSelf().InSingletonScope(); - Jaider
实际上,你的第一个选项不应该是一个选项,因为单例模式不允许有公共构造函数。 - Zé Carlos
2个回答

29

在设置绑定时,您需要绑定依赖项。最好在绑定中设置依赖项,而不是在构造函数中执行 kernel.Get<T>()。使用IOC,利用您正在使用的框架为您进行注入。

在您的第二个示例绑定中,您缺少了对 IDemoInterface 的绑定。您的绑定应该如下所示:

//bind the dependency to the implementation.
kernel.Bind<IDemoInterface>().To<DemoInterface>();
//since you bound your dependency, ninject should now have 
// all the dependencies required to instantiate your `MyFacade` object.
kernel.Bind<MyFacade>().To<MyFacade>().InSingletonScope(); 

这正是我所想的。还有一件事,为了避免直接实例化,通常单例使用私有构造函数...在Ninject中是否可能? - Jaider
据我所知,使用Ninject无法实现。Ninject需要一个公共构造函数来确定如何实例化要注入的对象。 - Joe Brunscheon
请查看有关默认公共构造函数及其解决方法的讨论:http://stackoverflow.com/questions/21665839/ninject-injecting-singleton - Joe Brunscheon
@Jaider 我知道这是一个老问题,但使用Ninject的方法仍然可以避免公开可用的构造函数,就是让类实现一个公共接口,但本身是internal。负责Ninject绑定的项目需要被授予访问相关内部的权限。 - Tobias Kildetoft

6
如果您不想使用InSingletonScope()让容器管理您的单例对象的生命周期,但仍希望注入该对象,我可以想到两种方法。请根据您的需要选择其中一种。考虑以下ISingleton(为您的接口命名)实现:
public class ConcreteSingleton : ISingleton
{
    private static readonly Lazy<ConcreteSingleton> _instance = new Lazy<ConcreteSingleton>(() => new ConcreteSingleton());

    private ConcreteSingleton() { }

    public static ConcreteSingleton Instance
    {
        get
        {
            return _instance.Value;
        }
    }
}
  1. Alter the singleton class to have a GetInstance(...) method

    In this method (my preferred approach), you won't be calling kernel.Inject(instance) each time, only for the first time the singleton is initialized. Adding the following method to your ConcreteSingleton class:

    public static ConcreteSingleton GetInstance(IKernel kernelForInjection)
    {
        if (_instance.IsValueCreated == false)
        {
            kernelForInjection.Inject(_instance.Value);
        }
    
        return _instance.Value;
    }
    

    And using this binding:

    kernel.Bind<ISingleton>().ToMethod(c => ConcreteSingleton.GetInstance(c.Kernel));
    

    Will achieve the desired behavior of not having a public constructor but enabling your facade to be efficiently injected.

  2. Perform injection each time the ISingleton instance is requested

    If by any reason you are not allowed to modify your ConcreteSingleton: This approach will wrap the singleton creation in a provider to efficiently inject the instance only for the first time it is created. It is important to note that the provider itself must be registered as a singleton.

    internal class ConcreteSingletonProvider : Provider<ISingleton>
    {
        public IKernel Kernel { get; set; }
    
        //Just a wrapper
        private readonly Lazy<ISingleton> _lazy = new Lazy<ISingleton>(() => ConcreteSingleton.Instance);
    
        public ConcreteSingletonProvider(IKernel kernel)
        {
            Kernel = kernel;
        }
    
        protected override ISingleton CreateInstance(IContext context)
        {
            if (_lazy.IsValueCreated == false)
            {
                Kernel.Inject(ConcreteSingleton.Instance);
            }
            return _lazy.Value;
        }
    }
    

    And your bindings should be like this:

    kernel.Bind<ISingleton>().ToProvider<ConcreteSingletonProvider>();
    kernel.Bind<ConcreteSingletonProvider>().ToSelf().InSingletonScope();
    

    This gist has a complete working sample for the above approach.

希望这能帮到你!

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