Konfigurieren von Simple Injector zum Ausführen von Hintergrundthreads in ASP.NET MVC

8

Ich verwende Simple Injector, um die Lebensdauer meiner injizierten Abhängigkeiten zu verwalten (in diesem Fall UnitOfWork ), und ich bin sehr froh, dass ein separater Decorator und nicht mein Service oder Command-Handler nach dem Speichern und Entsorgen Code macht viel einfacher beim Schreiben von Business-Logik-Schichten (ich folge der Architektur, die in dieser Blog-Post ).

Das obige funktioniert perfekt (und sehr einfach), indem Sie das MVC NuGet-Paket und den folgenden Code während des Aufbaus des Kompositionswurzelbehälters verwenden, wenn mehr als eine Abhängigkeit im Diagramm vorhanden ist - Perfekt für Entity Framework-Modellkontext.

%Vor%

Ich muss jetzt einige Hintergrundthreads ausführen und von Simple Injector verstehen Dokumentation zu Threads , dass Befehle wie folgt weitergegeben werden können:

%Vor%

ThreadedCommandHandlerProxy:

%Vor%

Allerdings kann ich aus dieser Threading-Beispieldokumentation sehen, dass Factories verwendet werden. Wenn ich Factories in meine Befehle und Service-Layer einfüge, werden die Dinge verwirrt und inkonsistent, da ich verschiedene Speichermethoden für verschiedene Services habe instanziierte Fabriken innerhalb von Diensten handhaben rettet und entsorgt) - Sie können sehen, wie klar und einfach das Service-Code-Skelett ohne irgendwelche Fabriken ist:

%Vor%

Mit dem oben genannten beginnt sich mein Problem zu bilden, wie ich aus der Dokumentation zu ASP.NET PerWebRequest Lebensdauern verstehe Der folgende Code wird verwendet:

%Vor%

Das obige funktioniert gut für jede HTTP-Anfrage, es wird eine gültige HttpContext.Current geben, aber wenn ich einen neuen Thread mit der ThreadedCommandHandlerProxy hochspiele, wird ein neuer Thread erstellt und die HttpContext wird nicht mehr existieren dieser Thread.

Da HttpContext bei jedem nachfolgenden Aufruf null wäre, wären alle Instanzen von Objekten, die in Dienstkonstruktoren eingefügt wurden, neu und eindeutig, im Gegensatz zur normalen HTTP pro Webanforderung, bei der Objekte in allen Diensten korrekt als dieselbe Instanz verwendet werden .

Also, um das obige in Fragen zusammenzufassen:

Wie würde ich die Objekte konstruieren und gemeinsame Objekte injizieren, unabhängig davon, ob sie aus einer HTTP-Anfrage oder aus einem neuen Thread erstellt wurden?

Gibt es spezielle Überlegungen, ob ein UnitOfWork von einem Thread in einem Command-Handler-Proxy verwaltet wird? Wie kann man sicherstellen, dass es nach der Ausführung des Handlers gespeichert und entsorgt wird?

Wenn wir ein Problem innerhalb des Befehls-Handlers / Service-Layers hatten und das UnitOfWork nicht speichern wollten, würden wir einfach eine Ausnahme werfen? Wenn ja, ist es möglich, dies auf globaler Ebene zu erfassen, oder müssen wir die Ausnahme pro Anfrage innerhalb von try - catch im Handler Decorator oder Proxy abfangen?

Danke,

Chris

    
g18c 14.06.2012, 21:27
quelle

2 Antworten

7

Lassen Sie mich mit der Warnung beginnen, dass Sie, wenn Sie Befehle asynchron in einer Webanwendung ausführen möchten, einen Schritt zurückgehen und sich ansehen möchten, was Sie erreichen möchten. Es besteht immer das Risiko, dass die Webanwendung unmittelbar nach dem Start des Handlers in einem Hintergrundthread wiederverwendet wird. Wenn eine ASP.NET-App wiederverwendet wird, werden alle Hintergrundthreads abgebrochen. Es wäre vielleicht besser, Befehle in einer (transaktionalen) Warteschlange zu veröffentlichen und sie von einem Hintergrunddienst abholen zu lassen. Dies stellt sicher, dass Befehle nicht verloren gehen können. Außerdem können Sie Befehle erneut ausführen, wenn der Handler nicht erfolgreich ausgeführt werden konnte. Es kann Ihnen auch ersparen, einige bösartige Registrierungen durchführen zu müssen (die Sie wahrscheinlich haben werden, egal welches DI-Framework Sie auswählen), aber dies ist wahrscheinlich nur ein Nebenproblem. Und wenn Sie Async-Handler ausführen müssen, versuchen Sie zumindest, die Anzahl der Handler, die Sie async ausführen, zu minimieren.

Damit brauchen Sie Folgendes:

Wie Sie bereits angemerkt haben, können Sie, da Sie (einige) Befehls-Handler asynchron ausführen, den Lifestyle für die Web-Anfrage nicht verwenden. Sie benötigen eine hybride Lösung, die zwischen einer Webanfrage und etwas anderem mischt. Das ist wahrscheinlich ein pro Lebensdauerumfang . Es gibt keine integrierten Erweiterungen für diese Hybrid-Lösungen, aus verschiedenen Gründen. 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.

In Simple Injector 2 wurde die Klasse Lifestyle hinzugefügt, die eine CreateHybrid -Methode enthält, die es ermöglicht, zwei beliebige Lebensstile zu kombinieren, um einen neuen Lebensstil zu kreieren. Hier ist ein Beispiel:

%Vor%

Sie können diesen hybriden Lebensstil verwenden, um die Arbeitseinheit zu registrieren:

%Vor%

Da Sie die Arbeitseinheit als lebenslanger Gültigkeitsbereich registrieren, müssen Sie einen lebenslangen Gültigkeitsbereich für einen bestimmten Thread explizit erstellen und löschen. Am einfachsten ist es, dies zu Ihrem ThreadedCommandHandlerProxy hinzuzufügen. Dies ist nicht die SOLID Art, Dinge zu tun, aber es ist der einfachste Weg für mich, Ihnen zu zeigen, wie man das macht.

  

Wenn wir ein Problem in der Befehls-Handler / Service-Schicht und hatten   wollte das UnitOfWork nicht retten, würden wir einfach einen werfen   Ausnahme?

Das typische Ding ist, eine Ausnahme zu werfen. Es ist in der Tat die allgemeine Regel der Ausnahmen:

  

Wenn Ihre Methode nicht das tun kann, was der Name verspricht, werfen Sie. - & gt;

Der Befehls-Handler sollte unwissend sein, wie der Kontext, in dem er ausgeführt wird, ist, und das Letzte, was Sie wollen, ist zu unterscheiden, ob er eine Ausnahme auslösen soll. Werfen ist also die beste Option. Wenn Sie jedoch mit einem Hintergrund-Thread arbeiten, sollten Sie diese Ausnahme besser verstehen, da .NET die gesamte Anwendungsdomäne abbricht, wenn Sie sie nicht abfangen. In einer Webanwendung bedeutet dies eine AppDomain-Wiederverwendung, was bedeutet, dass Ihre Webanwendung (oder zumindest dieser Server) für kurze Zeit offline ist.

Andererseits möchten Sie auch keine Ausnahmeinformationen verlieren. Daher sollten Sie diese Ausnahme protokollieren und wahrscheinlich eine serialisierte Darstellung dieses Befehls mit dieser Ausnahme protokollieren, damit Sie sehen können, welche Daten übergeben wurden Wenn es zur Methode ThreadedCommandHandlerProxy.Handle hinzugefügt wird, sieht es folgendermaßen aus:

%Vor%

Ich warnte davor, dass das asynchrone Ausführen von Handlern nicht die beste Idee wäre. Da Sie jedoch dieses Befehl / Handler-Muster anwenden, können Sie später auf die Verwendung von Warteschlangen umschalten, ohne eine einzelne Codezeile in Ihrer Anwendung ändern zu müssen. Es geht nur darum, eine Art QueueCommandHandlerDecorator<T> zu schreiben (wodurch der Befehl serialisiert und an die Warteschlange gesendet wird) und die Art und Weise zu ändern, in der die Dinge im Kompositionswurzel verkabelt sind, und es ist gut zu gehen (und natürlich nicht Vergessen Sie nicht, den Dienst zu implementieren, der Befehle aus der Warteschlange ausführt. Mit anderen Worten, das Tolle an diesem SOLID-Design ist, dass die Implementierung dieser Features auf die Größe der Anwendung abgestimmt ist.

    
Steven 15.06.2012, 23:45
quelle
5

Teil der Probleme beim Ausführen von Hintergrundthreads in ASP.NET können mit einem praktischen Befehlshandler-Decorator behoben werden:

%Vor%

Wenn Sie diesen Decorator zwischen einen Befehlshandler und die ThreadedCommandHandlerProxy stellen, stellen Sie sicher, dass (unter normalen Bedingungen) AppDomain nicht entladen wird, während ein solcher Befehl ausgeführt wird.

    
Steven 10.08.2012 10:26
quelle