14

Ich verwende SimpleInjector als meine IoC-Bibliothek. Ich registriere DbContext wie per Web-Anfrage und es funktioniert gut. Aber es gibt eine Aufgabe, die ich es in einem Hintergrundthread ausführe. Also, ich habe ein Problem, DbContext Instanzen zu erstellen. z.B.Gemischter Lebensstil für Pro Thread und pro Web Anfrage mit einfachem Injektor

  1. Service1 hat eine Instanz von DbContext
  2. Service2 hat eine Instanz von DbContext
  3. Service1Service2 und laufen von Hintergrund-Thread.
  4. Service1 holt eine Einheit und übergeben es an Service2
  5. Service2 verwendet diese Einheit, sondern Einheit losgelöst von DbContext

Eigentlich ist das Problem hier: Service1.DbContext ist der Unterschied von Service2.DbContext.

Es scheint, wenn ich eine Aufgabe in einem separaten Thread in ASP.NET MVC ausführen, SimpleInjector erstellt eine neue Instanz von DbContext für jeden Anruf. Während einige IoC-Bibliotheken (zum Beispiel StructureMap) einen gemischten Lebensstil für pro-Thread-pro-Web-Anfrage haben, scheint es SimpleInjector hat nicht einen. Habe ich recht?

Haben Sie eine Idee, dieses Problem in SimpleInjector zu lösen? Vielen Dank im Voraus.

EDIT:

Meine Leistungen sind hier:

class Service1 : IService1 { 
    public Service1(MyDbContext context) { } 
} 

class Service2 : IService2 { 
    public Service2(MyDbContext context, IService1 service1) { } 
} 

class SyncServiceUsage { 
    public SyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from HttpContext.Current 
    } 
} 

class AsyncServiceUsage { 
    public AsyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from background thread 
    } 
} 

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand { 

    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Func<ICommandHandler<TCommand>> factory) { 
     _factory = factory; 
    } 

    public void Handle(TCommand command) { 
     ThreadPool.QueueUserWorkItem(_ => { 
      // Create new handler in this thread. 
      var handler = _factory(); 
      handler.Handle(command); 
     }); 
    } 
} 

void InitializeSimpleInjector() { 
    register AsyncCommandHandlerDecorator for services (commands actually) that starts with "Async" 
} 

I Benutzer Service2 manchmal und AsyncService2 anderen Zeiten.

+0

Verwandte: http://stackoverflow.com/questions/11041601/simple-injector-multi-threading-in-mvc3-asp-net – Steven

+0

Verwandte: http://stackoverflow.com/questions/10304023/simpleinjector-is -dies-der-richtige-weg-zu-registriermanyforopengeneric-when-ich-have – Steven

Antwort

17

Es scheint, wenn ich eine Aufgabe in einem separaten Thread in ASP.NET MVC ausführen, erstellt SimpleInjector eine neue Instanz von DbContext für jeden Anruf.

Das Verhalten des RegisterPerWebRequest Lebensstil von Simple Injector v1.5 und unten ist eine vorübergehende Instanz zurück, wenn Instanzen außerhalb des Kontexts einer Web-Anfrage angefordert werden (wo HttpContext.Current null ist). Die Rückgabe einer vorübergehenden Instanz war ein Konstruktionsfehler in Simple Injector, da dies das Verbergen von fehlerhafter Verwendung erleichtert. Version 1.6 of the Simple Injector löst eine Ausnahme aus, statt eine vorübergehende Instanz falsch zurückzusenden, um klar zu kommunizieren, dass Sie den Container falsch konfiguriert haben.

Während einige IoC Bibliotheken (zB StructureMap) für einen gemischten Lebensstil haben pro-thread-per-webrequest, so scheint es, einfacher Injector nicht ein

Es ist richtig, hat die einfache Injector hat aus mehreren Gründen keine integrierte Unterstützung für gemischte Lebensstile. Vor allem ist es eine exotische Eigenschaft, die nicht viele Leute brauchen. Zweitens können Sie zwei oder drei Lebensstile zusammen mischen, so dass es fast eine endlose Kombination von Hybriden wäre.Und zuletzt ist es (ziemlich) einfach, das selbst zu registrieren.

Obwohl Sie Per Web Request mit Per Thread Lebensstile mischen kann, wäre es wahrscheinlich besser, wenn Sie mit Per Lifetime Scope, da mit dem Lifetime Scope explizit starten und beenden den Umfang (und kann die DbContext entsorgen, wenn der Umfang endet) Per Web anfordern mischen .

Von Simple Injector 2 und weiter, können Sie problemlos eine beliebige Anzahl von Lebensstilen mit der Lifestyle.CreateHybrid-Methode mischen. Hier ein Beispiel:

var hybridLifestyle = Lifestyle.CreateHybrid(
    () => HttpContext.Current != null, 
    new WebRequestLifestyle(), 
    new LifetimeScopeLifestyle()); 

// Register as hybrid PerWebRequest/PerLifetimeScope. 
container.Register<DbContext, MyDbContext>(hybridLifestyle); 

Es gibt eine andere Stackoverflow Frage, die in diesem Thema geht ein wenig tiefer, möchten Sie vielleicht einen Blick nehmen: Simple Injector: multi-threading in MVC3 ASP.NET

UPDATE

Über das Update. Du bist fast am Ziel. Die Befehle, die auf einem Hintergrundthread ausgeführt werden, müssen innerhalb eines Lifetime Scope ausgeführt werden, so dass Sie es explizit starten müssen. Der Trick besteht darin, BeginLifetimeScope auf dem neuen Thread aufzurufen, aber bevor der eigentliche Befehls-Handler (und seine Abhängigkeiten) erstellt wird. Mit anderen Worten, der beste Weg, dies zu tun, ist in einem Dekorateur.

Die einfachste Lösung ist Ihr AsyncCommandHandlerDecorator zu aktualisieren, den Umfang hinzuzufügen:

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     ThreadPool.QueueUserWorkItem(_ => 
     { 
      using (_container.BeginLifetimeScope()) 
      { 
       // Create new handler in this thread 
       // and inside the lifetime scope. 
       var handler = _factory(); 
       handler.Handle(command); 
      } 
     }); 
    } 
} 

Puristen, dass die SOLID Prinzipien schreien dafür einsetzen, dass diese Klasse die Single Responsibility Principle verstößt, da diese Dekorateur beide Befehle auf einem neuen Thread läuft und startet einen neuen lebenslangen Umfang. Ich würde mir darüber nicht viele Gedanken machen, da ich denke, dass es eine enge Beziehung zwischen dem Starten eines Hintergrund-Threads und dem Starten eines lebenslangen Bereichs gibt (du würdest sowieso keinen ohne den anderen verwenden). Aber immer noch, könnte man leicht die AsyncCommandHandlerDecorator unberührt lassen und eine neue LifetimeScopedCommandHandlerDecorator wie folgt erstellen:

public class LifetimeScopedCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public LifetimeScopedCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     using (_container.BeginLifetimeScope()) 
     { 
      // The handler must be created inside the lifetime scope. 
      var handler = _factory(); 
      handler.Handle(command); 
     } 
    } 
} 

Die Reihenfolge, in der diese Dekorateure registriert sind, ist natürlich wesentlich, da die AsyncCommandHandlerDecoratormuss die LifetimeScopedCommandHandlerDecorator wickeln. Dies bedeutet, dass die LifetimeScopedCommandHandlerDecorator Registrierung zuerst kommen muss:

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(LifetimeScopedCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(AsyncCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

This old Stackoverflow question spricht über diese im Detail. Du solltest auf jeden Fall einen Blick darauf werfen.

+1

Danke + Steven Ich las den anderen Link und es ist nützlich und hilfreich. Außerdem habe ich die Antwort gewählt und alle Fragen gestellt, um den Ruf von "SimpleInjector" zu verbessern. Viel Glück –

+0

WTF ich verwirrt! Ich bearbeite meine Frage. Kannst du mir bitte etwas über die Benutzung erzählen? Die Objekte "Service1" und "Service2" werden sowohl im Hauptthread (Web-Request) als auch im Hintergrundthread verwendet. Ich möchte das schaffen können. Und ich injiziere 'DbContext' mit ctor injection. Soll ich meine Implementierung ändern? –

+1

@Javad_Amiry: Tut mir leid für meine späte Antwort, aber selbst Entwickler brauchen manchmal etwas Ruhe ;-). Ich habe meine Antwort aktualisiert. Ich hoffe das hilft. – Steven

Verwandte Themen