Welche Registrierung Sie benötigen, hängt von der Art der Anwendung ab Ion. Da Sie über zwei verschiedene Frameworks sprechen (MVC und WinForms), haben beide eine unterschiedliche Registrierung.
Für eine MVC-Anwendung (oder Webanwendungen im Allgemeinen) ist es am häufigsten, die Arbeitseinheit auf einem per web request basis zu registrieren. Zum Beispiel wird die folgende Anmeldung die Arbeitseinheit während eines einzelnen Web-Anfrage-Cache:
container.Register<IUnitOfWork>(() =>
{
var items = HttpContext.Current.Items;
var uow = (IUnitOfWork)items["UnitOfWork"];
if (uow == null)
{
items["UnitOfWork"] = uow = container.GetInstance<UnitOfWork>();
}
return uow;
});
Der Nachteil dieser Anmeldung ist, dass die Arbeitseinheit nicht angeordnet ist (falls erforderlich). Es gibt an extension package für den Simple Injector, der dem Container RegisterPerWebRequest
Erweiterungsmethoden hinzufügt, wodurch automatisch sichergestellt wird, dass die Instanz am Ende der Webanforderung bereitgestellt wird. Mit diesem Paket können Sie die folgende Registrierung tun:
container.RegisterPerWebRequest<IUnitOfWork, UnitOfWork>();
, die eine Abkürzung ist:
container.Register<IUnitOfWork, UnitOfWork>(new WebRequestLifestyle());
Eine Windows Forms-Anwendung auf der anderen Seite ist in der Regel Single-Threaded (ein einzelnes Benutzer wird diese Anwendung verwenden). Ich glaube, dass es nicht ungewöhnlich ist, eine einzige Arbeitseinheit pro Formular zu haben, die in der Form geschlossen ist, aber mit dem Befehl/Handler-Muster denke ich, dass es besser ist, einen serviceorientierten Ansatz zu wählen. Was ich damit meine ist, dass es gut wäre, es so zu gestalten, dass Sie die Business-Schicht in einen WCF-Dienst verschieben können, ohne Änderungen an der Präsentationsebene vornehmen zu müssen. Sie können dies erreichen, indem Sie Ihre Befehle nur Primitive und (andere) DTO s enthalten lassen. Speichern Sie daher Entity Framework-Entitäten nicht in Ihren Befehlen, da dies die Serialisierung des Befehls erheblich erschwert und später zu Überraschungen führt.
Wenn Sie dies tun, wäre es bequem, eine neue Arbeitseinheit zu erstellen, bevor der Command-Handler die Ausführung startet, dieselbe Arbeitseinheit während der Ausführung dieses Handlers wieder verwendet und sie festschreibt, wenn der Handler erfolgreich abgeschlossen wurde (und immer entsorgen). Dies ist ein typisches Szenario für die Per Lifetime Scope lifestyle. Es gibt an extension package, die dem Container RegisterLifetimeScope
Erweiterungs-Methoden hinzufügt. Mit diesem Paket können Sie die folgende Registrierung tun:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();
, die eine Abkürzung ist:
container.Register<IUnitOfWork, UnitOfWork>(new LifetimeScopeLifestyle());
Die Registrierung ist jedoch nur die Hälfte der Geschichte. Der zweite Teil besteht darin, zu entscheiden, wann die Änderungen der Arbeitseinheit gespeichert werden sollen, und im Falle der Verwendung des Lifetime Scope-Lebensstils, wo ein solcher Bereich gestartet und beendet wird. Da Sie vor der Ausführung des Befehls explizit einen Gültigkeitszeitraum für die Gültigkeitsdauer starten sollten und ihn beenden sollen, wenn der Befehl ausgeführt wurde, verwenden Sie am besten einen Befehlshandler-Decorator, der die Befehlsroutinen umbrechen kann. Daher würden Sie normalerweise für die Formularanwendung einen zusätzlichen Befehlshandler-Decorator registrieren, der den Lebensdauerumfang verwaltet. Dieser Ansatz funktioniert in diesem Fall nicht. Werfen Sie einen Blick auf die folgenden Dekorateur, aber bitte beachten Sie, dass es falsch ist:
private class LifetimeScopeCommandHandlerDecorator<T>
: ICommandHandler<T>
{
private readonly Container container;
private readonly ICommandHandler<T> decoratedHandler;
public LifetimeScopeCommandHandlerDecorator(...) { ... }
public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
// WRONG!!!
this.decoratedHandler.Handle(command);
}
}
}
Dieser Ansatz nicht nicht funktioniert, weil die eingerichtete Befehlshandler erstellt wird vor die Lebensdauer Umfang gestartet wird.
Wir könnten, zu versuchen, versucht sein, dieses Problem zu lösen, wie folgt, aber das ist nicht richtig, entweder:
using (this.container.BeginLifetimeScope())
{
// EVEN MORE WRONG!!!
var handler = this.container.GetInstance<ICommandHandler<T>>();
handler.Handle(command);
}
Obwohl eine ICommandHandler<T>
innerhalb Kontext eines ganzen Lebens Rahmen anfordern, in der Tat ein nicht injizieren IUnitOfWork
für In diesem Bereich gibt der Container einen Handler zurück, der (wieder) mit einem LifetimeScopeCommandHandlerDecorator<T>
dekoriert ist. Das Aufrufen von handler.Handle(command)
wird daher zu einem rekursiven Aufruf führen, und wir werden mit einer Stapelüberlaufausnahme enden.
Das Problem besteht darin, dass das Abhängigkeitsdiagramm bereits erstellt wurde, bevor wir den Lebensdauerbereich starten können. Wir müssen daher den Abhängigkeitsgraphen aufbrechen, indem wir den Rest des Graphen verzögern. Der beste Weg, dies zu tun, damit Sie Ihr Anwendungsdesign sauber halten können, besteht darin, den Decorator in einen Proxy zu verwandeln und eine Factory in ihn einzufügen, die den Typ erzeugt, den er umbrechen sollte. Solche LifetimeScopeCommandHandlerProxy<T>
wird wie folgt aussehen:
// This class will be part of the Composition Root of
// the Windows Forms application
private class LifetimeScopeCommandHandlerProxy<T> : ICommandHandler<T>
{
// Since this type is part of the composition root,
// we are allowed to inject the container into it.
private Container container;
private Func<ICommandHandler<T>> factory;
public LifetimeScopeCommandHandlerProxy(Container container,
Func<ICommandHandler<T>> factory)
{
this.factory = factory;
this.container = container;
}
public void Handle(T command)
{
using (this.container.BeginLifetimeScope())
{
var handler = this.factory();
handler.Handle(command);
}
}
}
durch einen Delegierten Injektion, können wir die Zeit verzögern die Instanz erstellt wird und wir durch diese Weise den Aufbau (der Rest), um die Abhängigkeitsgraphen verzögern. Der Trick besteht nun darin, diese Proxy-Klasse so zu registrieren, dass sie die umhüllten Instanzen einschleusen wird, anstatt sich (natürlich) erneut zu injizieren. Simple Injector unterstützt das Einfügen von Func<T>
Fabriken in Dekoratoren, so dass Sie einfach die RegisterDecorator
und in diesem Fall sogar die RegisterSingleDecorator
Erweiterungsmethode verwenden können.
Beachten Sie, dass die Reihenfolge, in der Decorators (und dieser Proxy) registriert sind (offensichtlich) wichtig ist. Da dieser Proxy einen neuen lebenslangen Gültigkeitsbereich startet, sollte er den Decorator umbrechen, der die Arbeitseinheit festlegt.Mit anderen Worten würde eine vollständigere Registrierung wie folgt aussehen:
container.RegisterLifetimeScope<IUnitOfWork, UnitOfWork>();
container.RegisterManyForOpenGeneric(
typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
// Register a decorator that handles saving the unit of
// work after a handler has executed successfully.
// This decorator will wrap all command handlers.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
// Register the proxy that starts a lifetime scope.
// This proxy will wrap the transaction decorators.
container.RegisterSingleDecorator(
typeof(ICommandHandler<>),
typeof(LifetimeScopeCommandHandlerProxy<>));
Registrieren des Proxy und Dekorateur umgekehrt bedeuten würde, dass die TransactionCommandHandlerDecorator<T>
auf einem anderen IUnitOfWork
als der Rest des Abhängigkeitsgraphen abhängen würde der Fall ist, die würde bedeuten, dass alle Änderungen an der Arbeitseinheit in diesem Diagramm nicht übernommen werden. Mit anderen Worten, Ihre Anwendung wird nicht mehr funktionieren. Überprüfen Sie diese Registrierung daher immer sorgfältig.
Viel Glück.