Alla ricerca di uno scope Ninject che si comporta come InRequestScope

Sul mio livello di servizio ho iniettato un UnitOfWork e 2 repository nel costruttore. L’unità di lavoro e il repository hanno un’istanza di un DbContext che voglio condividere tra loro due. Come posso farlo con Ninject? Quale ambito dovrebbe essere considerato?

Non sono in un’applicazione Web, quindi non posso usare InRequestScope .

Cerco di fare qualcosa di simile … e sto usando DI tuttavia, ho bisogno che il mio UoW sia Dispose d e creato in questo modo.

 using (IUnitOfWork uow = new UnitOfWorkFactory.Create()) { _testARepository.Insert(a); _testBRepository.Insert(b); uow.SaveChanges(); } 

EDIT: Voglio solo essere sicuro di aver capito … dopo un’occhiata a https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope ho pensato alla mia attuale architettura applicativa della console che effettivamente usa Ninject.

Diciamo :

La class A è una class del livello di servizio

La class B è un’unità di lavoro che prende in parametro un’interfaccia (IContextFactory)

La class C è un repository che prende in parametro un’interfaccia (IContextFactory)

L’idea qui è di essere in grado di fare operazioni di contesto su 2 o più repository e usare l’unità di lavoro per applicare le modifiche.

La Classe D è una fabbrica di contesto (Entity Framework) che fornisce un’istanza (conservare in un contenitore) del contesto che è condivisa tra Class B et C (.. e sarebbe anche per altri repository).

Il factory di contesto mantiene l’istanza nel suo contenitore, quindi non voglio riutilizzare questa istanza tutto il nome poiché il contesto deve essere eliminato alla fine dell’operazione di servizio. In realtà è lo scopo principale di InNamedScope?

La soluzione sarebbe, ma non sono sicuro che lo stia facendo bene, l’istanza dei servizi sarà transitoria, il che significa che in realtà non sono mai disposti? :

 Bind() .To() .InNamedScope("ServiceScope") .WithConstructorArgument( "connectionString", ConfigurationUtility.GetConnectionString()); Bind().To(); Bind().To(); Bind().To(); Bind().To().DefinesNamedScope("ServiceScope"); Bind().To().DefinesNamedScope("ServiceScope"); 

    AGGIORNAMENTO: Questo approccio funziona contro NuGet corrente, ma si basa su un’anomalia nell’implementazione di InCallscope che è stata corretta nei pacchetti NuGet Unstable correnti. Farò una modifica a questa risposta tra qualche giorno per riflettere l’approccio migliore dopo alcuni rimugini. NB il modo di strutturare roba di alto livello rimarrà praticamente identico, solo i dettagli esatti della Bind() funzioneranno. (Suggerimento: CreateNamedScope in unstable funzionerebbe o si potrebbe impostare il Command Handler come DefinesNamedScope . Ragione Non lo faccio solo perché voglio avere qualcosa che InRequestScope / giochi bene con InRequestScope )


    Consiglio vivamente di leggere i test di integrazione di Ninject.Extensions.NamedScope (seriamente, trovarli e leggerli e rileggerli)

    DbContext è un’Unità di lavoro quindi non è necessario alcun ulteriore involucro.

    Poiché vuoi essere in grado di avere più “richieste” in volo e vuoi avere una singola unità di lavoro condivisa tra loro, devi:

     Bind() .ToMethod( ctx => new DbContext( connectionStringName: ConfigurationUtility.GetConnectionString() )) .InCallScope(); 

    InCallScope() significa che:

    1. per un dato object grafico composto per un singolo kernel.Get() Call (quindi In Call Scope), tutti quelli che richiedono un DbContext otterranno la stessa istanza.
    2. l’ IDisposable . Dispose() verrà chiamato quando si verifica un Kernel.Release() per l’object radice (o un Kernel.Components.Get().Clear() verifica per la radice se non lo è .InCallScope() )

    Non ci dovrebbero essere motivi per usare InNamedScope() e DefinesNamedScope() ; Non hai oggetti a vita lunga che stai cercando di escludere dal pooling / parenting / grouping predefinito.

    Se fai quanto sopra, dovresti essere in grado di:

     var command = kernel.Get(); try { command.Execute(); } finally { kernel.Components.Get().Clear( command ); // Dispose of DbContext happens here } 

    L’implementazione del comando è simile a:

     class Command : ICommand { readonly IAccountRepository _ar; readonly IBlockedIpRepository _br; readonly DbContext _ctx; public Command(IAccountRepository ar, IBlockedIpRepository br, DbContext ctx){ _ar = ar; _br = br; _ctx = ctx; } void ICommand.Execute(){ _ar.Insert(a); _br.Insert(b); _ctx.saveChanges(); } } 

    Nota che in generale, evito di avere un’Unità di Lavoro implicita in questo modo, e invece di farne emergere la creazione e lo Disposal . Questo rende un comando simile a questo:

     class Command : ICommand { readonly IAccountService _as; readonly IBlockedIpService _bs; readonly Func _createContext; public Command(IAccountService @as, IBlockedIpServices bs, Func createContext){ _as = @as; _bs = bs; _createContext = createContext; } void ICommand.Execute(){ using(var ctx = _createContext()) { _ar.InsertA(ctx); _br.InsertB(ctx); ctx.saveChanges(); } } 

    Ciò non comporta l’uso di .InCallScope() sul Bind() (ma richiede la presenza del Ninject.Extensions.Factory di Ninject.Extensions.Factory per sintetizzare Func da un semplice Bind() .

    Come discusso nell’altra risposta , InCallScope non è un buon approccio per risolvere questo problema.

    Per ora sto scaricando del codice che funziona contro l’ultima NuGet Unstable / Include PreRelease / Instal-Package -Pre edizioni di Ninject.Web.Common senza una chiara spiegazione. Lo Ninject.Extensions.NamedScope in un articolo del wiki Ninject.Extensions.NamedScope a un certo punto ha iniziato a scrivere una procedura dettagliata di questa tecnica Ninject.Extensions.NamedScope CreateNamedScope / GetScope del wiki di Ninject.Extensions.NamedScope .

    Probabilmente alcuni bit diventeranno richieste di pull anche ad un certo punto (suggerimento per @Remo Gloor che mi ha fornito il codice di contorno). I test associati e i test di apprendimento sono in questo momento per ora ), in attesa di un imballaggio in un formato TBD appropriato.

    Il sumrio exec è Caricare il modulo sottostante nel proprio kernel e utilizzare .InRequestScope() su tutto ciò che si desidera creare / Dispose d per l’invocazione del gestore e quindi IHandlerComposer.ComposeCallDispose richieste tramite IHandlerComposer.ComposeCallDispose .

    Se si utilizza il seguente modulo:

     public class Module : NinjectModule { public override void Load() { Bind().To(); // Wire it up so InRequestScope will work for Handler scopes Bind().To(); NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn( Kernel ); } } 

    Quali fili in una fabbrica [1] e in NinjectHttpApplicationPlugin che espone:

     public interface INinjectRequestHandlerScopeFactory { NamedScope CreateRequestHandlerScope(); } 

    Quindi puoi usare questo Composer per eseguire una richiesta InRequestScope() :

     public interface IHandlerComposer { void ComposeCallDispose( Type type, Action callback ); } 

    Implementato come:

     class NinjectRequestScopedHandlerComposer : IHandlerComposer { readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory; public NinjectRequestScopedHandlerComposer( INinjectRequestHandlerScopeFactory requestHandlerScopeFactory ) { _requestHandlerScopeFactory = requestHandlerScopeFactory; } void IHandlerComposer.ComposeCallDispose( Type handlerType, Action callback ) { using ( var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope() ) foreach ( object handler in resolutionRoot.GetAll( handlerType ) ) callback( handler ); } } 

    Le cose di Ninject Infrastructure:

     class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory { internal const string ScopeName = "Handler"; readonly IKernel _kernel; public NinjectRequestHandlerScopeFactory( IKernel kernel ) { _kernel = kernel; } NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope() { return _kernel.CreateNamedScope( ScopeName ); } ///  /// When plugged in as a Ninject Kernel Component via RegisterIn(IKernel), makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's .InRequestScope() Binding extension. ///  public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin { readonly IKernel kernel; public static void RegisterIn( IKernel kernel ) { kernel.Components.Add(); } public NinjectHttpApplicationPlugin( IKernel kernel ) { this.kernel = kernel; } object INinjectHttpApplicationPlugin.GetRequestScope( IContext context ) { // TODO PR for TrgGetScope try { return NamedScopeExtensionMethods.GetScope( context, ScopeName ); } catch ( UnknownScopeException ) { return null; } } void INinjectHttpApplicationPlugin.Start() { } void INinjectHttpApplicationPlugin.Stop() { } } }