Способы настройки синглтона 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 имеет полный рабочий образец для вышеуказанного подхода.
Надеюсь, что это поможет!