Способы настройки синглтона Ninject

У меня есть класс (MyFacade), который я ввел параметр с помощью Ninject:

class MyFacade
{
    IDemoInterface demo;

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

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

Конечно, мне нужно настроить Ninject, чтобы ввести подходящую реализацию моего параметра (IDemoInterface)

Я знаю, я могу создать экземпляр объекта MyFacade, выполнив kernel.Get<MyFacade>(); без установки чего-либо еще. В настоящее время мой фасад не имеет интерфейса (потому что это моя единственная реализация, возможно, я добавлю его интерфейс для стандартных предложений)

если я хочу сделать этот синглтон для фасада, я знаю два пути: создайте пустой конструктор и передайте параметр, выполнив это kernel.Get<IDemoInterface>(); или установив Ninject как: kernel.Bind<MyFacade>().To<MyFacade>().InSingletonScope();

Второй выглядит лучше, но знаете ли вы каким-либо другим способом настроить его одним способом?

Ответы

Ответ 1

При настройке привязок вам необходимо привязать свои зависимости. Всегда лучше настраивать зависимости в ваших привязках, а не делать 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(); 

Ответ 2

Если вы не хотите, чтобы контейнер управлял жизненным циклом вашего синглтона, используя 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;
        }
    }
}
  • Изменить класс singleton, чтобы иметь GetInstance(...) метод

    В этом методе (мой предпочтительный подход) вы не будете называть kernel.Inject(instance) каждый раз, только в первый раз инициализируется singleton. Добавьте следующий класс в класс ConcreteSingleton:

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

    И используя эту привязку:

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

    Достигнет желаемого поведения, не имея публичного конструктора, но позволяющего эффективно вводить ваш фасад.

  • Выполнять инъекцию каждый раз, когда запрашивается экземпляр ISingleton

    Если по какой-либо причине вам не разрешено изменять ваш ConcreteSingleton: этот подход завершает создание одноэлементного кода в провайдере, чтобы эффективно вводить экземпляр только в первый раз, когда он создается. Важно отметить, что сам поставщик должен быть зарегистрирован как одиночный.

    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;
        }
    }
    

    И ваши привязки должны быть такими:

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

    Этот gist имеет полный рабочий образец для вышеуказанного подхода.

Надеюсь, что это поможет!