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 AsyncCommandHandlerDecorator
muss 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.
Verwandte: http://stackoverflow.com/questions/11041601/simple-injector-multi-threading-in-mvc3-asp-net – Steven
Verwandte: http://stackoverflow.com/questions/10304023/simpleinjector-is -dies-der-richtige-weg-zu-registriermanyforopengeneric-when-ich-have – Steven