2013-04-30 18 views
5

sto usando Ninject e le estensioni EventBroker e DependencyCreation in un'applicazione MVC 3. Ho installato e sto usando il pacchetto Ninject.MVC3 e quindi lo OnePerRequestModule.Ninject - Richiesta portata è già stato smaltito

Sto tentando di iniettare un servizio, chiamato IParentService in un controller. IParentService ha una dipendenza su ChildService creata tramite l'estensione DependencyCreation (senza riferimento fisso).

Entrambi i servizi sono registrati in un'istanza mediatore evento locale (locale per ParentService).

Voglio il IParentService per limitare l'ambito per ogni richiesta e voglio la dipendenza e broker di evento per essere smaltiti allo stesso tempo come il IParentService, però, sto diventando un ScopeDisposedException. Cosa sto sbagliando?

Alcuni codice:

Servizio Definizioni:

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) 
    {    
    } 
} 

Kernel di registrazione (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");    
    } 

traccia stack:

0.123.516,41 mila
[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 - MAGGIORI DETTAGLI

L'errore viene generato all'interno di un delegato di disattivazione che è impostato nella chiamata a RegisterOnEventBroker, dove il codice tenta di annullare la registrazione di tutti gli oggetti registrati sul mediatore dell'evento. Fallisce perché l'ambito del broker di eventi è stato eliminato, presumibilmente perché il servizio padre è stato eliminato. Per quanto ne so, Ninject chiamerà i delegati di OnDeactivation solo per oggetti con durata diversa da quella transitoria, quindi perché questo non funziona quando il servizio parent è registrato in RequestScope mi confonde. L'ambito transitorio non è sufficiente per il servizio parent perché si verificano perdite di memoria a causa di questo problema.

Sto iniziando a chiedermi se questo è un bug nell'estensione EventBroker.

risposta

2

È necessario prima associare IParentService a ParentService quindi utilizzare auto-binding di tipo concreto kernel.Bind<ParentService>().ToSelf() di definire il campo Oggetto e broker di evento.

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"); 
    }  

Modificato: se il tipo si sta risolvendo è un tipo concreto (come ParentService sopra), Ninject creerà automaticamente un'associazione predefinita attraverso un meccanismo chiamato vincolante sé implicita. Ti piace questa:

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

D'altra parte impliciti auto-attacchi sono generati nel perimetro oggetto predefinito che è Transient. Questo è il motivo per cui il tuo codice non viene eseguito nell'ambito Request.

Per ulteriori informazioni vedere here

modificato 2:

C'è un errore in bbvEventBroker estensione in Request portata che causano EventBroker stata ceduta prima di smaltire l'oggetto che registra su tale EventBroker. Pertanto, nel metodo OnDeactivation dell'oggetto non è presente EventBroker che può essere richiamato dal suo Unregister e generato 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; 
    } 

Si può vedere in OwnsEventBroker metodo NamedScope definire nel campo di applicazione dell'oggetto (ParentService) competenti per smaltire prima che l'oggetto (ParentService).

D'altra parte in OnDeactivation dell'oggetto (ParentService) è necessario EventBroker che ha disposto in precedenza.

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

La soluzione sta creando l'albero oggetto con NamedScope. Definisci genitore nello scope Request mentre definisce un NamedScope per i suoi figli (Publisher/Subscriber) e possiede il broker di eventi (OwnsEventBroker). Quindi definire un server di pubblicazione (ChildService1) e un Sottoscrittore (ChildService2) nell'ambito denominato è stato definito dal genitore. In questo modo è possibile garantire che il proprietario del broker dell'evento sarà eliminato dopo il loro childen.

+0

Grazie Kambiz, questo risolve l'errore. Puoi approfondire un po 'di più su cosa c'era di sbagliato nel modo in cui lo stavo facendo e perché è necessario farlo in questo modo? – nukefusion

+0

@nukefusion Ho aggiornato la risposta controllandola –

+0

Ho raddoppiato l'errore ma nel mio test sto iniettando IParentService non il tipo concreto. Questo sembrava risolvere il mio problema, ma in realtà era solo usando la prima rilegatura invece della seconda associazione "auto". Se utilizzo il tuo codice e inserisco un'istanza 'ParentService', ottengo la stessa eccezione. – nukefusion

2

Il nucleo Ninject attualmente disattiva gli oggetti che sono nell'ambito di un oggetto prima di disattivare l'oggetto stesso.

La modifica dell'ordine sembra risolvere il problema. Sebbene prima di spingere quel cambiamento devo controllare quali effetti collaterali questo può avere per altre situazioni.

+0

Grazie per l'esame di questo Remo. C'è un problema aperto per questo, quindi posso seguirlo? – nukefusion

Problemi correlati