请求范围已经被处理
本文关键字:处理 范围 请求 | 更新日期: 2023-09-27 18:11:29
我在MVC 3应用程序中使用Ninject
和扩展EventBroker和DependencyCreation。我已经安装并正在使用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
EDIT - MORE DETAILS
该错误在RegisterOnEventBroker
调用中设置的去激活委托中抛出,其中代码试图取消注册在事件代理上注册的任何对象。失败的原因是事件代理范围已被处置,可能是因为父服务已被处置。据我所知,Ninject只会调用OnDeactivation委托的对象与生命周期以外的瞬态作用域,所以为什么这不起作用,当父服务在RequestScope
中注册让我困惑。暂态作用域对于父服务是不够的,因为我遇到了内存泄漏,因为这个问题。
我开始怀疑这是否是EventBroker扩展中的一个错误。
您必须首先将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:
在Request
范围内bbvEventBroker
扩展中有一个bug,导致EventBroker在处置在该EventBroker上注册的对象之前被处置。因此,在对象的OnDeactivation
方法中没有EventBroker,它的Unregister可以被调用,ScopeDisposedException
可以被抛出。
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
)之前进行dispose。
另一方面,在对象(ParentService
)的OnDeactivation
中,需要先前处置的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
作用域中定义父作用域,同时为其子作用域(Publisher/Subscriber)定义NamedScope
,并拥有事件代理(OwnsEventBroker
)。然后在父节点定义的命名范围中定义发布者(ChildService1
)和订阅者(ChildService2
)。通过这种方式,您可以确保事件代理的所有者将在其子事件之后被处置。
Ninject core当前在取消激活对象本身之前取消激活对象范围内的对象。
改变顺序似乎可以解决这个问题。尽管在进行更改之前,我必须检查这对其他情况可能产生的副作用