6

Auf meiner Service-Ebene habe ich eine UnitOfWork und 2 Repositories in den Konstruktor injiziert. Die Unit of Work und das Repository haben eine Instanz von DbContext, die ich zwischen den beiden teilen möchte. Wie kann ich das mit Ninject machen? Welcher Umfang sollte berücksichtigt werden?Suche nach einem Ninject-Bereich, der sich wie InRequestScope verhält

Ich bin nicht in einer Webanwendung so kann ich InRequestScope nicht verwenden.

Ich versuche, etwas ähnliches zu tun ... und ich benutze DI, aber ich brauche meine UoW Dispose d und erstellt so.

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

    uow.SaveChanges(); 
} 

EDIT: Ich möchte nur sicher sein, ich verstehe ... nach Blick auf https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope i obwohl über meine aktuellen Konsolenanwendungsarchitektur, die Ninject tatsächlich nutzen.

Können sagen:

Klasse A ist eine Service-Schicht Klasse

Klasse B eine Arbeitseinheit ist, die Parameter nehmen in eine Schnittstelle (IContextFactory)

Klasse C ist ein Behälter, der in dauern Parameter eine Schnittstelle (IContextFactory)

Die Idee hier ist in der Lage sein, Kontextoperationen auf 2 oder mehr Repository zu tun und die Arbeitseinheit verwenden, um die Änderungen anzuwenden.

Klasse D ist eine Kontextfactory (Entity Framework), die eine Instanz (in einem Container enthalten) des Kontextes bereitstellt, der von Klasse B und C gemeinsam genutzt wird (.. und auch für andere Repositories).

Die Kontextfactory behält die Instanz in ihrem Container, sodass ich diese Instanz nicht alle Namen verwenden möchte, da der Kontext am Ende der Serviceoperation angeordnet werden muss. Dies ist der Hauptzweck des InNamedScopes tatsächlich ?

Die Lösung wäre, aber ich bin mir nicht sicher, ich mache es richtig, die Dienstleistungen Instanz wird transcient sein, was bedeutet, dass sie tatsächlich nie veräußert? :

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

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

+1

bezogen werden: http://StackOverflow.com/Questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594 –

Antwort

5

UPDATE: Dieser Ansatz funktioniert gegen NuGet Strom, sondern verlässt sich in einer Anomalie in der InCallscope Implementierung, die in den aktuellen Unstable NuGet Pakete behoben wurde. Ich werde diese Antwort in ein paar Tagen optimieren, um den besten Ansatz nach einigem Nachdenken zu reflektieren. NB die hochgradige Art der Strukturierung wird ziemlich identisch bleiben, nur die genauen Details der Bind<DbContext>() Scoping wird funktionieren. (Hinweis:. CreateNamedScope in instabilen funktionieren würde, oder man könnte auf den Befehl Handler als DefinesNamedScope einrichten Grund Ich nicht nur das tun, ist, dass ich etwas haben will, die komponiert/spielt gut mit InRequestScope)


ich sehr empfehlen die Ninject.Extensions.NamedScope Integrationstests lesen (ernst, finden sie und liest und liest sie)

Die DbContextist eine Arbeitseinheit, so dass keine weitere Verpackung notwendig.

Wie Sie mehrere Anfragen "im Flug haben zu können und wollen eine einzelne Arbeitseinheit haben, zwischen ihnen geteilt, müssen Sie:

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

Die InCallScope() bedeutet:

  1. für einen bestimmten Objektgraph zusammengesetzt für ein einzelnes kernel.Get()Anruf (daher In Anruf Scope), jeder, der eine DbContext erfordert die sa erhalten ich Instanz.
  2. die IDisposable. Dispose() wird aufgerufen, wenn ein Kernel.Release() für das Root-Objekt geschieht (oder ein Kernel.Components.Get<ICache>().Clear() geschieht für die Wurzel, wenn es nicht .InCallScope() ist)

Es sollte kein Grund sein InNamedScope() und DefinesNamedScope() zu verwenden; Sie haben keine langlebigen Objekte, die Sie aus dem standardmäßigen Pooling/Parenting/Gruppieren ausschließen möchten.

Wenn Sie die oben tun, sollten Sie in der Lage sein:

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

Die Befehls Umsetzung wie folgt aussieht:

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

Beachten Sie, dass im Allgemeinen, vermeide ich eine implizite Einheit Arbeiten Sie auf diese Weise, und stattdessen Oberfläche ist die Erstellung und Disposal. Dies macht einen Befehl wie folgt aussehen:

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

Dies keine Verwendung von .InCallScope() auf dem Bind<DbContext>() beinhaltet (aber das Vorhandensein von Ninject.Extensions.Factory's FactoryModule erfordert die Func<DbContext> von einem einfachen Bind<DbContext>()

+0

Ich kann Singleton nicht verwenden .. Ursache die Dienste befinden sich in Befehle, die pro Threads erstellt werden. Dies ist ein Spieleserver. Aber das InNamedScope scheint das zu sein, wonach ich gesucht habe ... wenn das Scope-Ende andere Instanzen ausführt (siehe obiges Beispiel), die zwar transzient sind, aber Teil der Instanz sind, die einen Bereich hat, der irgendwie entfernt wird? Oder muss ich Release aufrufen, nachdem der Service ausgeführt wurde? Was passiert, wenn die Elterninstanz GC erhält? – Rushino

+0

Danke das ein sehr schönes Beispiel. Ich frage mich nur, was passiert, wenn der Kompositionswurzel nicht genau so ist? Ich meine, ich habe keinen Zugriff auf den Kernel .. außer wenn ich den Service Locator benutze. Irgendeine Idee ? Alles beginnt mit einem Singleton .. Ich integriere meine Architektur über eine andere Bibliothek .. und der einzige Einstiegspunkt, den ich gefunden habe, ist die Befehle (Serverabfragen, wo ich meine Dienste injiziere). Also im Grunde sind sie alle einmal instanciert .. und Anfragen werden an sie weitergeleitet. – Rushino

+0

@Rushino Nicht sicher, was du hier meinst. Vielleicht sollte eine separate Frage mit einer Illustration Ihrer Einschränkungen in Ordnung sein. Erstens können Sie https://github.com/ninject/ninject.extensions.factory/wiki verwenden, um entsprechend eingeschränkten Zugriff auf die Erstellung von Befehls-Handlern usw. an der richtigen Stelle in Ihrem Verarbeitungszyklus zu gewähren. z.B. 'kernel.Get ();' kann Ihnen ein 'ICommandFactory' (mit einem' ICommand CreateCommand() ') zur Verfügung gestellt werden, um Ihre' ServerQueryProcessor' 'Serverabfrage' Implementierung. Mit anderen Worten, Ihr Abfrageprozessor wird zu einem Kompositionswurzel. –

2

Wie diskutiert in the other answer, zu synthetisieren. InCallScope ist kein guter Ansatz, um dieses Problem zu lösen

Für jetzt bin ich dumping etwas Code, der gegen die neuesten NuGet Instable/Include PreRelease/Instal-Package -Pre arbeitet Ausgaben von Ninject.Web.Common ohne eine klare Erklärung. I wird dies in einen Artikel in der Ninject.Extensions.NamedScope Wiki zu irgendeinem Zeitpunkt haben begonnen, eine Walkthrough dieser Technik in the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article zu schreiben.

Möglicherweise werden einige Bits irgendwann auch zu Pull Requests. (Tipp an @Remo Gloor, der mir den Outline-Code geliefert hat). Die associated tests and learning tests are in this gist for now), ausstehende Verpackung in einem ordnungsgemäß freigegebenen Format TBD.

Die exec Zusammenfassung laden Sie das Modul unten in Ihren Kernel und verwenden Sie für alles, was Sie erstellen möchten/Dispose d per Handler Aufruf und dann Anfragen über IHandlerComposer.ComposeCallDispose füttern.

Wenn Sie die folgenden Module:

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

welche Drähte in einer Fabrik [1] und NinjectHttpApplicationPlugin, die entlarvt:

public interface INinjectRequestHandlerScopeFactory 
{ 
    NamedScope CreateRequestHandlerScope(); 
} 

Dann können Sie diese Composer verwenden, um eine Anfrage zu starten InRequestScope():

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

Implementiert als:

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

Die Ninject Infrastruktur Zeug:

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