Подтвердить что ты не робот

Обнаружение нулевого объекта - используйте один и тот же экземпляр для построения всего графика

Скажем, у меня есть следующие классы, которые я хочу построить, используя Ninject, со стрелками, показывающими зависимости.

A > B > D
A > C > D

Я хочу настроить Ninject таким образом, чтобы A был временным, т.е. каждый раз, когда вы запрашиваете Ninject для A, вы получаете новый. Я также хочу, чтобы B и C были временными, вы получаете новый из них каждый раз, когда вы просите A. Но я хочу, чтобы D был повторно использован как для B, так и для C. Поэтому каждый раз, когда я запрашиваю A, я хочу Ninject для построения одного из каждого объекта, а не двух Ds. Но я не хочу, чтобы Ds повторно использовался в разных As.

Каков наилучший способ установить это с помощью Ninject?

Update:
После еще нескольких исследований кажется, что Unity имеет PerResolveLifetimeManager, который делает то, что я ищу. Существует ли эквивалент Ninject?

4b9b3361

Ответ 1

Ninject поддерживает четыре встроенных области видимости из коробки: Transient, Singleton, Thread, Request.

Таким образом, не существует PerResolveLifetimeManager подобной области видимости, но вы можете легко реализовать ее при регистрации настраиваемой области с помощью метода InScope.

Как оказалось, существует существующее расширение Ninject: ninject.extensions.namedscope, которое предоставляет метод InCallScope, который является тем, что вы ищете.

Однако, если вы хотите сделать это самостоятельно, вы можете сделать с пользовательским делегатом InScope. Где вы можете использовать основной объект IRequest для типа A для использования в качестве объекта области видимости:

var kernel = new StandardKernel();
kernel.Bind<A>().ToSelf().InTransientScope();
kernel.Bind<B>().ToSelf().InTransientScope();
kernel.Bind<C>().ToSelf().InTransientScope();
kernel.Bind<D>().ToSelf().InScope(
    c =>
        {
            //use the Request for A as the scope object                         
            var requestForA = c.Request;
            while (requestForA != null && requestForA.Service != typeof (A))
            {
                requestForA = requestForA.ParentRequest;
            }
            return requestForA;
        });

var a1 = kernel.Get<A>();    
Assert.AreSame(a1.b.d, a1.c.d);

var a2 = kernel.Get<A>();    
Assert.AreSame(a2.b.d, a2.c.d);

Assert.AreNotSame(a1.c.d, a2.c.d);

Если классы образцов:

public class A
{
    public readonly B b;
    public readonly C c;
    public A(B b, C c) { this.b = b; this.c = c; }
}

public class B
{
    public readonly D d;
    public B(D d) { this.d = d; }
}

public class C
{
    public readonly D d;  
    public C(D d) { this.d = d; }
}

public class D { }

Ответ 2

Я нашел решение моей конкретной проблемы, которая является InCallScope, которая предоставляется расширением ninject.extensions.namedscope. Это ведет себя идентично концепции Unity PerResolveLifetimeManager.

Ответ 3

Один из вариантов заключается в создании зависимостей самостоятельно.

var kernel = new StandardKernel();
kernel.Bind<A>().ToMethod(ctx =>
{
    var d = new D();
    var c = new C(d);
    var b = new B(d);
    var a = new A(b, c);
    return a;
});

Это может быть не предпочтительный метод, но он всегда будет создавать новый экземпляр A, используя новый экземпляр B, C и D (но повторно используя тот же экземпляр D для B и C). Вы можете добавить вызов InTransientScope(), но это не требуется, поскольку это область по умолчанию.