【问题标题】:Ninject - Request scope has already been disposedNinject - 请求范围已被释放
【发布时间】:2013-04-30 19:52:40
【问题描述】:

我在 MVC 3 应用程序中使用 Ninject 和扩展 EventBroker 和 DependencyCreation。我已经安装并正在使用 Ninject.MVC3 包,因此是 OnePerRequestModule

我正在尝试将名为IParentService 的服务注入控制器。 IParentService 依赖于通过 DependencyCreation 扩展创建的 ChildService(无硬引用)。

这两个服务都在本地事件代理实例(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 只会为具有 Transient 范围以外的生命周期的对象调用 OnDeactivation 委托,所以为什么在 RequestScope 中注册父服务时这不起作用让我感到困惑。瞬态范围对于父服务来说是不够的,因为我正因为这个问题而遇到内存泄漏。

我开始怀疑这是否是 EventBroker 扩展中的错误。

【问题讨论】:

    标签: c# asp.net-mvc ninject ninject.web.mvc ninject-extensions


    【解决方案1】:

    您必须先将IParentService绑定到ParentService,然后使用具体类型kernel.Bind&lt;ParentService&gt;().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 范围内运行的原因。

    欲了解更多信息,请参阅here

    编辑 2:

    Request 范围内的 bbvEventBroker 扩展存在一个错误,导致 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)。通过这种方式,您可以确保事件代理的所有者将在他们的孩子之后被处置。

    【讨论】:

    • 感谢 Kambiz,这确实解决了错误。您能否详细说明我这样做的方式有什么问题以及为什么需要这样做?
    • 我已经加倍检查了这一点,但在我的测试中,我注入的是 IParentService 而不是具体类型。这似乎解决了我的问题,但实际上它只是使用您的第一个绑定而不是第二个“自我”绑定。如果我使用你的代码并注入一个“ParentService”实例,我会得到同样的异常。
    • @nukefusion 你是对的;我认为这是错误。我发现当 Scope 设置为 Request 时,EventBorker NamedScope 被释放,而 ParentService 处于释放阶段。当OnDeactivation尝试调用ctx.ContextPreservingGet&lt;IEventBroker&gt;(eventBrokerName)获取EventBroker实例时,抛出异常。
    【解决方案2】:

    Ninject 核心当前在停用对象本身之前停用对象范围内的对象。

    更改顺序似乎可以解决该问题。尽管在推动该更改之前,我必须检查这可能对其他情况产生什么副作用。

    【讨论】:

    • 感谢您查看此 Remo。是否有未解决的问题,以便我可以跟踪它?
    猜你喜欢
    • 1970-01-01
    • 2015-02-05
    • 2012-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 2012-08-12
    相关资源
    最近更新 更多