6

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

Sono non in un'applicazione Web quindi non posso utilizzare InRequestScope.

Provo a fare qualcosa di simile ... e sto usando DI tuttavia, ho bisogno che il mio UoW sia Dispose d 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 i se circa il mio attuale architettura delle applicazioni di console che in realtà uso Ninject.

consente di dire:

Classe A è una classe livello di servizio

Classe B è un unità di lavoro che tengano parametro un'interfaccia (IContextFactory)

Classe C è un repository che tengano parametrizzare un'interfaccia (IContextFactory)

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

Classe D è una factory di contesto (Entity Framework) che fornisce un'istanza (keep in a container) 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 .. è lo scopo principale di InNamedScope in realtà ?

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<IScsContextFactory>() 
    .To<ScsContextFactory>() 
    .InNamedScope("ServiceScope") 
    .WithConstructorArgument(
     "connectionString", 
     ConfigurationUtility.GetConnectionString()); 

Bind<IUnitOfWork>().To<ScsUnitOfWork>(); 

Bind<IAccountRepository>().To<AccountRepository>(); 
Bind<IBlockedIpRepository>().To<BlockedIpRepository>(); 

Bind<IAccountService>().To<AccountService>().DefinesNamedScope("ServiceScope"); 
Bind<IBlockedIpService>().To<BlockedIpService>().DefinesNamedScope("ServiceScope"); 
+0

correlati: http://stackoverflow.com/questions/14554151/dependency-injection-and-life-time-of-idisposable-objects –

+1

correlati: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594 –

risposta

5

UPDATE: Questo approccio funziona contro corrente NuGet, ma si basa in un'anomalia nel InCallscope attuazione che è stato fissato nelle attuali pacchetti Nuget instabili. 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 dello scope Bind<DbContext>() funzioneranno. (Suggerimento:. CreateNamedScope in unstable avrebbe funzionato o si potrebbe impostare il gestore di comandi come DefinesNamedScope Motivo I dont appena faccio è che voglio avere qualcosa che compone/gioca bene con InRequestScope)


mi raccomando la lettura dei test di integrazione Ninject.Extensions.NamedScope (sul serio, li trovare e leggere e rileggere)

Il DbContextè un'unità di lavoro in modo che nessun ulteriore involucro è necessaria.

Come si vuole essere in grado di avere più 'richieste' in volo e vogliono avere una singola unità di lavoro condiviso tra di loro, è necessario:

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

Il InCallScope() significa che:

  1. per un dato oggetto grafico composto per un singolo kernel.Get()chiamata (da qui in chiamata Scope), tutti che richiede un DbContext otterrà la sa esempio.
  2. il IDisposable. Dispose() sarà chiamata quando un Kernel.Release() accade per l'oggetto principale (o una Kernel.Components.Get<ICache>().Clear() accade per la radice se non è .InCallScope())

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

Se fate quanto sopra, si dovrebbe essere in grado di:

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

L'implementazione di comando si presenta come:

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

Si noti che, in generale, ho evitare di avere un implicito Unità di lavoro in questo modo, e invece di superficie è la creazione e Disposal. Questo rende un comando simile a questa:

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

Questo comporta nessun utilizzo di .InCallScope() sul Bind<DbContext>() (ma richiede la presenza di Ninject.Extensions.Factory's FactoryModule di sintetizzare il Func<DbContext> da un semplice Bind<DbContext>()

+0

Non riesco a utilizzare il singleton .. perché i servizi si trovano in comandi creati per thread. Questo è un server di gioco. Ma l'InNamedScope sembra essere quello che stavo cercando .. quando il fine dell'oscilloscopio fa altre istanze (vedi sopra l'esempio aggiunto) che sono transitori ma par dell'istanza che ha un ambito viene smaltito in qualche modo? o devo chiamare Release dopo che il servizio è stato eseguito? Cosa succede se l'istanza genitore ottiene GC? – Rushino

+0

Grazie che un bell'esempio. Mi chiedo solo ... cosa succede se la radice della composizione non è esattamente così? Voglio dire, non ho accesso al kernel ... tranne se utilizzo il localizzatore di servizi. Qualche idea ? Tutto parte da un singleton .. Sto integrando la mia architettura su qualche altra libreria .. e l'unico punto di accesso che ho trovato sono i comandi (query del server dove iniettare i miei servizi). Quindi in pratica sono tutti istanziati una volta .. e le richieste vengono reindirizzate a loro. – Rushino

+0

@Rushino Non sei sicuro di cosa intendi qui. Forse una domanda separata con un'illustrazione dei tuoi vincoli potrebbe essere in ordine. In primo luogo, è possibile utilizzare https://github.com/ninject/ninject.extensions.factory/wiki per fornire un accesso opportunamente vincolato alla creazione di Command Handler, ecc. Al punto giusto del ciclo di elaborazione. per esempio. 'kernel.Get ();' può diventare come se avessi un 'ICommandFactory' (con un' ICommand CreateCommand() ') fornito all'implementazione della 'query del server'' ServerQueryProcessor' '. In altre parole, il processore di query diventa una radice di composizione. –

2

Come discusso nel the other answer,. InCallScope non è un buon approccio per risolvere questo problema

Per ora sto scaricando un codice che funziona contro l'ultimo NuGet Unstable/Include PreRelease/Instal-Package -Pre edizioni di Ninject.Web.Common senza una chiara spiegazione. I lo tradurrò in un articolo nel wiki Ninject.Extensions.NamedScope ad un certo punto hanno iniziato a scrivere una procedura dettagliata di questa tecnica in the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article.

Probabilmente alcuni bit diventeranno richieste di pull anche a un certo punto (Hat punta a @Remo Gloor che mi ha fornito il codice schema). associated tests and learning tests are in this gist for now), in attesa della confezione in un formato TBD appropriato.

La sintesi è exec si caricare il modulo qui sotto nel vostro kernel e utilizzare .InRequestScope() su tutto ciò che si desidera creato/Dispose d ogni handler di invocazione e poi nutrite richieste tramite via IHandlerComposer.ComposeCallDispose.

Se si utilizza il seguente modulo:

public class Module : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IHandlerComposer>().To<NinjectRequestScopedHandlerComposer>(); 

     // Wire it up so InRequestScope will work for Handler scopes 
     Bind<INinjectRequestHandlerScopeFactory>().To<NinjectRequestHandlerScopeFactory>(); 
     NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn(Kernel); 
    } 
} 

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

public interface INinjectRequestHandlerScopeFactory 
{ 
    NamedScope CreateRequestHandlerScope(); 
} 

quindi è possibile utilizzare questo compositore per l'esecuzione di una richiesta InRequestScope():

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

Implementato come:

class NinjectRequestScopedHandlerComposer : IHandlerComposer 
{ 
    readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory; 

    public NinjectRequestScopedHandlerComposer(INinjectRequestHandlerScopeFactory requestHandlerScopeFactory) 
    { 
     _requestHandlerScopeFactory = requestHandlerScopeFactory; 
    } 

    void IHandlerComposer.ComposeCallDispose(Type handlerType, Action<object> callback) 
    { 
     using (var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope()) 
      foreach (object handler in resolutionRoot.GetAll(handlerType)) 
       callback(handler); 
    } 
} 

La roba Infrastrutture Ninject:

class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory 
{ 
    internal const string ScopeName = "Handler"; 

    readonly IKernel _kernel; 

    public NinjectRequestHandlerScopeFactory(IKernel kernel) 
    { 
     _kernel = kernel; 
    } 

    NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope() 
    { 
     return _kernel.CreateNamedScope(ScopeName); 
    } 

    /// <summary> 
    /// When plugged in as a Ninject Kernel Component via <c>RegisterIn(IKernel)</c>, makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's <c>.InRequestScope()</c> Binding extension. 
    /// </summary> 
    public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin 
    { 
     readonly IKernel kernel; 

     public static void RegisterIn(IKernel kernel) 
     { 
      kernel.Components.Add<INinjectHttpApplicationPlugin, NinjectHttpApplicationPlugin>(); 
     } 

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