Ninject — область запроса уже удалена

Я использую Ninject и расширения EventBroker и DependencyCreation в приложении MVC 3. Я установил и использую пакет Ninject.MVC3, поэтому файл OnePerRequestModule.

Я пытаюсь внедрить службу под названием IParentService в контроллер. IParentService имеет зависимость от ChildService, созданного с помощью расширения DependencyCreation (без жесткой ссылки).

Обе службы зарегистрированы в локальном экземпляре брокера событий (локальном для ParentService).

Я хочу, чтобы область действия IParentService определялась для каждого запроса, и я хочу, чтобы брокер зависимостей и событий удалялся одновременно с IParentService, однако я получаю ScopeDisposedException. Что я делаю неправильно?

Некоторый код:

Определения службы:

public interface IParentService
{
}

public class ParentService : IParentService
{
    [EventPublication("topic://ParentService/MyEvent")]
    public event EventHandler<EventArgs> MyEvent;
}

public class ChildService
{
    [EventSubscription("topic://ParentService/MyEvent", typeof(bbv.Common.EventBroker.Handlers.Publisher))]
    public void OnMyEvent(object sender, EventArgs eventArgs)
    {            
    }
}

Регистрация ядра (NinjectWebCommon)

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IParentService>().To<ParentService>()
            .InRequestScope()
            .OwnsEventBroker("ParentServiceBroker")
            .RegisterOnEventBroker("ParentServiceBroker");

        kernel.DefineDependency<IParentService, ChildService>();
        kernel.Bind<ChildService>().ToSelf()
            .WhenInjectedInto<ParentService>()
            .InDependencyCreatorScope()
            .RegisterOnEventBroker("ParentServiceBroker");            
    }  

Трассировка стека:

[ScopeDisposedException: The requested scope has already been disposed.]
   Ninject.Extensions.NamedScope.NamedScopeExtensionMethods.GetScope(IContext context, String scopeParameterName) in c:\Projects\Ninject\ninject.extensions.namedscope\src\Ninject.Extensions.NamedScope\NamedScopeExtensionMethods.cs:118
   Ninject.Extensions.NamedScope.NamedScopeExtensionMethods.GetScope(IContext context, String scopeParameterName) in c:\Projects\Ninject\ninject.extensions.namedscope\src\Ninject.Extensions.NamedScope\NamedScopeExtensionMethods.cs:126
   Ninject.Extensions.NamedScope.<>c__DisplayClass1`1.<InNamedScope>b__0(IContext context) in c:\Projects\Ninject\ninject.extensions.namedscope\src\Ninject.Extensions.NamedScope\NamedScopeExtensionMethods.cs:40
   Ninject.Planning.Bindings.BindingConfiguration.GetScope(IContext context) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Bindings\BindingConfiguration.cs:119
   Ninject.Planning.Bindings.Binding.GetScope(IContext context) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Bindings\Binding.cs:224
   Ninject.Activation.Context.GetScope() in c:\Projects\Ninject\ninject\src\Ninject\Activation\Context.cs:123
   Ninject.Activation.Caching.Cache.TryGet(IContext context) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Caching\Cache.cs:110
   Ninject.Activation.Context.Resolve() in c:\Projects\Ninject\ninject\src\Ninject\Activation\Context.cs:150
   Ninject.<>c__DisplayClass10.<Resolve>b__c(IBinding binding) in c:\Projects\Ninject\ninject\src\Ninject\KernelBase.cs:386
   System.Linq.WhereSelectEnumerableIterator`2.MoveNext() +145
   System.Linq.<CastIterator>d__b1`1.MoveNext() +85
   System.Linq.Enumerable.Single(IEnumerable`1 source) +191
   Ninject.ResolutionExtensions.Get(IResolutionRoot root, String name, IParameter[] parameters) in c:\Projects\Ninject\ninject\src\Ninject\Syntax\ResolutionExtensions.cs:50
   Ninject.Extensions.ContextPreservation.ContextPreservationExtensionMethods.ContextPreservingGet(IContext context, String name, IParameter[] parameters) in c:\Projects\Ninject\ninject.extensions.contextpreservation\src\Ninject.Extensions.ContextPreservation\ContextPreservationExtensionMethods.cs:56
   Ninject.Extensions.bbvEventBroker.<>c__DisplayClass2`1.<RegisterOnEventBroker>b__0(IContext ctx, T instance) in c:\Projects\Ninject\ninject.extensions.bbveventbroker\src\Ninject.Extensions.bbvEventBroker\EventBrokerExtensionMethods.cs:45
   Ninject.Planning.Bindings.<>c__DisplayClass29`1.<OnDeactivation>b__28(IContext context, Object instance) in c:\Projects\Ninject\ninject\src\Ninject\Planning\Bindings\BindingConfigurationBuilder.cs:513
   Ninject.Activation.Strategies.<>c__DisplayClass4.<Deactivate>b__3(Action`2 action) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Strategies\BindingActionStrategy.cs:42
   Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map(IEnumerable`1 series, Action`1 action) in c:\Projects\Ninject\ninject\src\Ninject\Infrastructure\Language\ExtensionsForIEnumerableOfT.cs:32
   Ninject.Activation.Strategies.BindingActionStrategy.Deactivate(IContext context, InstanceReference reference) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Strategies\BindingActionStrategy.cs:42
   Ninject.Activation.<>c__DisplayClass6.<Deactivate>b__4(IActivationStrategy s) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Pipeline.cs:72
   Ninject.Infrastructure.Language.ExtensionsForIEnumerableOfT.Map(IEnumerable`1 series, Action`1 action) in c:\Projects\Ninject\ninject\src\Ninject\Infrastructure\Language\ExtensionsForIEnumerableOfT.cs:32
   Ninject.Activation.Pipeline.Deactivate(IContext context, InstanceReference reference) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Pipeline.cs:72
   Ninject.Activation.Caching.Cache.Forget(CacheEntry entry) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Caching\Cache.cs:253
   Ninject.Activation.Caching.Cache.Forget(IEnumerable`1 cacheEntries) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Caching\Cache.cs:242
   Ninject.Activation.Caching.Cache.Clear(Object scope) in c:\Projects\Ninject\ninject\src\Ninject\Activation\Caching\Cache.cs:197
   Ninject.Web.Common.<>c__DisplayClass2.<DeactivateInstancesForCurrentHttpRequest>b__1(IKernel kernel) in c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\OnePerRequestHttpModule.cs:74
   Ninject.GlobalKernelRegistration.MapKernels(Action`1 action) in c:\Projects\Ninject\ninject\src\Ninject\GlobalKernelRegistration.cs:75
   Ninject.Web.Common.OnePerRequestHttpModule.DeactivateInstancesForCurrentHttpRequest() in c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\OnePerRequestHttpModule.cs:74
   Ninject.Web.Common.OnePerRequestHttpModule.<Init>b__0(Object o, EventArgs e) in c:\Projects\Ninject\Ninject.Web.Common\src\Ninject.Web.Common\OnePerRequestHttpModule.cs:56
   System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69

ИЗМЕНИТЬ — ПОДРОБНЕЕ

Ошибка возникает в делегате деактивации, заданном при вызове RegisterOnEventBroker, где код пытается отменить регистрацию любых объектов, зарегистрированных в брокере событий. Это не удается, потому что область брокера событий была удалена, предположительно, из-за того, что родительская служба была удалена. Насколько мне известно, Ninject будет вызывать делегатов OnDeactivation только для объектов с временем жизни, отличным от области Transient, поэтому меня смущает, почему это не работает, когда родительская служба зарегистрирована в RequestScope. Временная область действия недостаточна для родительской службы, так как из-за этой проблемы возникают утечки памяти.

Я начинаю задаваться вопросом, является ли это ошибкой в ​​​​расширении EventBroker.


person Matt B    schedule 30.04.2013    source источник


Ответы (2)


Сначала необходимо привязать IParentService к ParentService, а затем использовать самопривязку конкретного типа kernel.Bind<ParentService>().ToSelf() для определения области объекта и брокера событий.

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IParentService>().To<ParentService>();


        kernel.Bind<ParentService>().ToSelf()
       .InRequestScope()
       .OwnsEventBroker("ParentServiceBroker")
       .RegisterOnEventBroker("ParentServiceBroker");

        kernel.DefineDependency<IParentService, ChildService>();
        kernel.Bind<ChildService>().ToSelf()
            .WhenInjectedInto<ParentService>()
            .InDependencyCreatorScope()
            .RegisterOnEventBroker("ParentServiceBroker"); 
    }    

Отредактировано: если тип, который вы разрешаете, является конкретным типом (например, ParentService выше), Ninject автоматически создаст ассоциацию по умолчанию с помощью механизма, называемого неявным самопривязыванием. Так:

 kernel.Bind<ParentService>().ToSelf();

С другой стороны, неявные самопривязки генерируются в области объекта по умолчанию, которая равна Transient. Вот почему ваш код не работает в области Request.

Для получения дополнительной информации см. здесь

Отредактировано 2:

В расширении bbvEventBroker в области Request есть ошибка, из-за которой EventBroker удалялся до удаления объекта, который регистрируется в этом EventBroker. Поэтому в OnDeactivation методе объекта нет EventBroker, у которого можно вызвать Unregister и ScopeDisposedException wan бросить.

    public static IBindingOnSyntax<T> OwnsEventBroker<T>(this IBindingOnSyntax<T> syntax, string eventBrokerName)
    {
        string namedScopeName = "EventBrokerScope" + eventBrokerName;
        syntax.DefinesNamedScope(namedScopeName);
        syntax.Kernel.Bind<IEventBroker>().To<EventBroker>().InNamedScope(namedScopeName).Named(eventBrokerName);
        syntax.Kernel.Bind<IEventBroker>().ToMethod(ctx => ctx.ContextPreservingGet<IEventBroker>(eventBrokerName)).WhenTargetNamed(eventBrokerName);
        return syntax;
    }

Вы можете видеть в методе OwnsEventBroker NamedScope, определяемом в области действия объекта (ParentService), который заставляет его располагаться перед объектом (ParentService).

С другой стороны, в OnDeactivation объекта (ParentService) нужен EventBroker, который был удален ранее.

    public static IBindingOnSyntax<T> RegisterOnEventBroker<T>(
        this IBindingOnSyntax<T> syntax, string eventBrokerName)
    {
        return
            syntax.OnActivation((ctx, instance) => ctx.ContextPreservingGet<IEventBroker>(eventBrokerName).Register(instance))
                  .OnDeactivation((ctx, instance) => ctx.ContextPreservingGet<IEventBroker>(eventBrokerName).Unregister(instance));
    }

EventBrokerExtensionMethods.cs

Решением является создание дерева объектов с NamedScope. Определите родителя в области Request, в то время как он определяет NamedScope для своих дочерних элементов (издатель/подписчик) и владеет брокером событий (OwnsEventBroker). Затем определите издателя (ChildService1) и подписчика (ChildService2) в именованной области, которые были определены родителем. Таким образом, вы можете гарантировать, что владелец брокера событий будет удален после своего дочернего элемента.

person Kambiz Shahim    schedule 08.05.2013
comment
Спасибо Камбиз, это устраняет ошибку. Не могли бы вы немного подробнее рассказать о том, что было не так с тем, как я это делал, и почему это нужно делать именно так? - person Matt B; 09.05.2013
comment
Я дважды проверил это, но в своем тесте я ввожу IParentService не конкретный тип. Это, казалось, решило мою проблему, но на самом деле это было просто использование вашей первой привязки вместо второй привязки «я». Если я использую ваш код и добавляю экземпляр ParentService, я получаю то же исключение. - person Matt B; 09.05.2013
comment
@nukefusion Вы правы; Я думаю, это ошибка. Я обнаружил, что когда Scope имеет значение Request, EventBorker NamedScope удаляется, а ParentService находится в стадии удаления. Когда OnDeactivation пытается вызвать ctx.ContextPreservingGet<IEventBroker>(eventBrokerName) для получения экземпляра EventBroker, возникает исключение. - person Kambiz Shahim; 10.05.2013

Ядро Ninject в настоящее время деактивирует объекты, которые находятся в области действия объекта, прежде чем деактивировать сам объект.

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

person Remo Gloor    schedule 11.05.2013
comment
Спасибо за внимание к этому Ремо. Есть ли открытая проблема для этого, чтобы я мог отслеживать ее? - person Matt B; 12.05.2013