Ich habe eine API-Anwendung, die mehrere Datenbank-Shards mit StructureMap für die Abhängigkeitsinjektion verwendet. Einer der erforderlichen Header in jedem API-Aufruf ist ein ShardKey
, der mir mitteilt, welche Datenbank dieser Aufruf adressiert. Um dies zu bewirken, ich habe eine Klasse namens OwinMiddleware
ShardingMiddleware
, die den folgenden Code enthält (aus Gründen der Übersichtlichkeit snipped):Cross-Thread-Konflikte in StructureMap
var nestedContainer = container.GetNestedContainer();
using (var db = MyDbContext.ForShard(shardKey)) // creates a new MyDbContext with connection string appropriate to shardKey
{
nestedContainer.Configure(cfg => cfg.For<MyDbContext>().Use(db));
await Next.Invoke(context);
}
Dies funktioniert wunderbar in meiner Testumgebung und übergibt eine Batterie von Integrationstests.
Aber die Integrationstests sind effektiv single-threaded. Als ich dies in eine QA-Umgebung implementierte, in der eine echte App mit mehreren simultanen Anrufen auf meine API trifft, beginnen die Dinge sich in eine birnenförmige Form zu verwandeln. Ferinstance:
System.ObjectDisposedException: Zugriff auf ein Objekt nicht möglich. Eine häufige Ursache für diesen Fehler ist die Bereitstellung eines Kontexts, der aus der Abhängigkeitsinjektion gelöst wurde und später an anderer Stelle in Ihrer Anwendung dieselbe Kontextinstanz verwenden soll. Dies kann auftreten, wenn Sie Dispose() für den Kontext aufrufen oder den Kontext in einer using-Anweisung umbrechen. Wenn Sie die Abhängigkeitsinjektion verwenden, sollten Sie den Dependency-Injektionscontainer veranlassen, Kontextinstanzen zu entfernen.
Oder andere Ausnahmen, die darauf hinweisen, dass StructureMap keine gültige Instanz von MyDbContext
zur Verfügung hat.
Für mich scheint es, dass die mehreren Threads irgendwie die Konfiguration gegenseitig durcheinander bringen, aber für das Leben von mir kann ich nicht verstehen, wie ich einen verschachtelten Container verwende, um den Datenbankkontext für jede API zu speichern Anruf.
Irgendwelche Ideen, was hier schief gehen könnte?
Update: Ich habe auch versucht, meine DB-Kontext in eine Schnittstelle zu abstrahieren. Hat keinen wirklichen Unterschied gemacht; Ich bekomme immer noch den Fehler
System.InvalidOperationException: Beim Versuch, einen Controller des Typs 'SomeController' zu erstellen, ist ein Fehler aufgetreten. Stellen Sie sicher, dass der Controller über einen parameterlosen öffentlichen Konstruktor verfügt. ---> StructureMap.StructureMapConfigurationException: Keine Standard-Instanz registriert ist und nicht automatisch für Typen
Update ‚MyNamespace.IMyDbContext‘ bestimmt werden kann 2: ich das Problem gelöst, aber die Prämie ist noch offen. Bitte sehen Sie meine Antwort unten.
Ihr 'DbContext' könnte als [Captive Dependency] am Leben erhalten werden (http://blog.ploeh.dk/2014/06/02/captive-dependency/). Stellen Sie sicher, dass Konsumenten dieser Abhängigkeit keine Lebensdauer haben, die länger ist als die des "DbContext" oder - besser - verhindern, dass der DbContext direkt in die Konsumenten injiziert wird. Ein 'DbContext' ist Runtime-Daten und Runtime-Daten [sollten nicht in Komponenten injiziert werden] (https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). Verstecken Sie stattdessen den DbContext hinter einer Abstraktion. – Steven