Препятствуйте тому, чтобы Ninject звонил, Инициализируют многократно при привязке с несколькими интерфейсами

У нас есть конкретный одноэлементный сервис, который реализует Ninject.IInitializable и 2 интерфейса. Проблема состоит в том, что сервисы Инициализируют-methdod, назван 2 раза, когда только один желаем. Мы используем.NET 3.5 и Ninject 2.0.0.0.

Есть ли шаблон в Ninject, предотвращают это. Ни одна из реализации интерфейсов Ninject.IInitializable. класс обслуживания:

public class ConcreteService : IService1, IService2, Ninject.IInitializable
{
    public void Initialize()
    {
        // This is called twice!
    }
}

И модуль похож на это:

public class ServiceModule : NinjectModule
{
    public override void Load()
    {
        this.Singleton<Iservice1, Iservice2, ConcreteService>();
    }
}

где Singleton является дополнительным методом, определенным как это:

    public static void Singleton<K, T>(this NinjectModule module) where T : K
    {
        module.Bind<K>().To<T>().InSingletonScope();
    }

    public static void Singleton<K, L, T>(this NinjectModule module) 
        where T : K, L
    {
        Singleton<K, T>(module);
        module.Bind<L>().ToMethod(n => n.Kernel.Get<T>());
    }

Конечно, мы могли добавить bool инициализированного участника к ConcreteService и инициализировать только, когда это - ложь, но это кажется довольно мало взлом. И это потребовало бы повторения той же логики в каждом сервисе, который реализует два или больше интерфейса.


Спасибо за все ответы! Я изучил что-то от всех них! (Мне приходится нелегко для решения который корректная метка).

Мы закончили тем, что создали интерфейс IActivable и расширили ninject ядро (это также удалило приятно зависимости от уровня кода к ninject, хотя атрибуты все еще остаются).

20
задан Ahe 15 June 2010 в 13:45
поделиться

3 ответа

Ninject 3

Ninject 3.0 теперь поддерживает несколько общих типов в вызове для привязки, то, что вы пытаетесь сделать, можно легко выполнить в одно цепочечное заявление.

kernel.Bind<IService1, IService2>()
      .To<ConcreteService>()
      .InSingletonScope();

Ninject 2

Вы устанавливаете две разные привязки K => T и L => T. Запрашивающие экземпляры L вернут временные экземпляры T. Запрос K вернет одноэлементный экземпляр T.

В Ninject 2.0 область объектов определяется для каждого интерфейса службы, привязанного к обратному вызову области.

Когда у вас есть

Bind<IFoo>...InSingletonScope();
Bind<IBar>...InSingletonScope();

, вы создаете два разных прицела.

Вы говорите "Привязка к IFoo приведет к тому же объекту, который был возвращен. когда вызвали .Get ". а также "Привязка к IBar приведет к тому же объекту, который был возвращен. когда .Get был вызван. "

вы можете связать привязки вместе, но вам нужно будет удалить IInitializable , так как это вызовет дублирующую инициализацию при активации экземпляра:

kernel.Bind<IBoo>()
      .To<Foo>()
      .InSingletonScope();
      .OnActivation(instance=>instance.Initialize());

kernel.Bind<IBaz>()
      .ToMethod( ctx => (IBaz) ctx.Kernel.Get<IBoo>() );

или

kernel.Bind<Foo>().ToSelf().InSingletonScope()
    .OnActivation(instance=>instance.Initialize());
kernel.Bind<IBaz>().ToMethod( ctx => ctx.Kernel.Get<Foo>() );
kernel.Bind<IBoo>().ToMethod( ctx => ctx.Kernel.Get<Foo>() );

в чтобы заставить несколько интерфейсов разрешаться в один и тот же экземпляр синглтона. Когда я вижу подобные ситуации, мне всегда приходится спрашивать, не слишком ли много делает ваш объект, если у вас есть синглтон с двумя обязанностями?

29
ответ дан 30 November 2019 в 00:31
поделиться

Обновление: совершенно уверен, что использование нескольких перегрузок Bind V3 решит эту проблему; См. этот вопрос / ответ


Хороший вопрос.

Если посмотреть на источник, бит инициализации происходит после каждого Activate . Ваш Bind ... ToMethod тоже считается одним. Стратегия применяется довольно единообразно - в отдельных случаях невозможно отказаться.

Ваш обходной путь - использовать явное OnActivation в вашем Bind , которое будет делать это условно (но для того, чтобы сделать это обычным способом, потребуется поддерживать Набор инициализированных объектов ( Я не смотрел, есть ли механизм, позволяющий прикрепить флаг к активированному объекту)), или сделать ваш идемпотентный вариант Initialize любыми наиболее удобными для вас средствами.

РЕДАКТИРОВАТЬ:

    internal interface IService1
    {
    }

    internal interface IService2
    {
    }

    public class ConcreteService : IService1, IService2, Ninject.IInitializable
    {
        public int CallCount { get; private set; }
        public void Initialize()
        {
            ++CallCount;
        }
    }

    public class ServiceModule : NinjectModule
    {
        public override void Load()
        {
            this.Singleton<IService1, IService2, ConcreteService>();
        }
    }

Учитывая следующие помощники:

static class Helpers
{
    public static void Singleton<K, T>( this NinjectModule module ) where T : K
    {
        module.Bind<K>().To<T>().InSingletonScope();
    }

    public static void Singleton<K, L, T>( this NinjectModule module )
        where T : K, L
    {
        Singleton<T, T>( module );
        module.Bind<K>().ToMethod( n => n.Kernel.Get<T>() );
        module.Bind<L>().ToMethod( n => n.Kernel.Get<T>() );
    }
}

@Ian Davis et al.Проблема в том, что:

    class Problem
    {
        [Fact]
        static void x()
        {
            var kernel = new StandardKernel( new ServiceModule() );
            var v1 = kernel.Get<IService1>();
            var v2 = kernel.Get<IService2>();
            var service = kernel.Get<ConcreteService>();
            Console.WriteLine( service.CallCount ); // 3
            Assert.AreEqual( 1, service.CallCount ); // FAILS
        }
    }

Потому что каждая активация (на Bind ) инициализируется каждый раз.

РЕДАКТИРОВАТЬ 2: То же самое, когда вы используете следующую немного урезанную версию:

static class Helpers
{
    public static void Singleton<K, L, T>( this NinjectModule module )
        where T : K, L
    {
        module.Bind<T>().ToSelf().InSingletonScope();
        module.Bind<K>().ToMethod( n => n.Kernel.Get<T>() );
        module.Bind<L>().ToMethod( n => n.Kernel.Get<T>() );
    }
}
2
ответ дан 30 November 2019 в 00:31
поделиться

Я думаю, что один из вариантов - вы создаете объект в модуле и связываете свой объект с каждым из интерфейсов.

BTW, постарайтесь не использовать специфичный для контейнеров код в вашем производственном коде. Если вам приходится это делать, используйте какие-нибудь помощники и изолируйте их в проекте модуля.

public class ServiceModule : NinjectModule
{

    public override void Load()
    { 
         ConcreteService svc = new ConcreteService();
         Bind<IService1>().ToConstant(svc);
         Bind<IService2>().ToConstant(svc);
         ....
     }
}
0
ответ дан 30 November 2019 в 00:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: