EF, ASP MVC + Abhängigkeitsinjektion. Probleme mit mehreren gleichzeitigen Anfragen und DB-Konnektivität

8

Ich arbeite an einem NopCommerce-basierten Projekt, das ASP MVC, Autofac und Entity Framework verwendet. Ich habe Ausnahmen, die beim Aufruf einer Methode für einen Dienst innerhalb einer MVC-Route auftreten, die einen Aufruf der DB mit Hilfe von EF macht.

Während der Entwicklung funktioniert alles einwandfrei - während des Belastungstests, wenn mehrere Benutzer gleichzeitig sind, werden 1 oder 2 Anfragen abstürzen, und einer der folgenden Fehler wird in ELMAH protokolliert.

  

System.InvalidOperationException ExecuteReader erfordert eine offene und verfügbare Verbindung. Der aktuelle Status der Verbindung ist offen.

     

System.InvalidOperationException ExecuteReader erfordert eine offene und verfügbare Verbindung. Der aktuelle Status der Verbindung ist geschlossen.

     

System.InvalidOperationException Die Verbindung wurde nicht geschlossen. Der aktuelle Status der Verbindung wird hergestellt.

     

System.ObjectDisposedException Die ObjectContext-Instanz wurde entfernt und kann nicht mehr für Operationen verwendet werden, die eine Verbindung erfordern.

     

System.ObjectDisposedException Die Operation kann nicht abgeschlossen werden, da der DbContext entfernt wurde.

     

System.ArgumentException Ein Element mit demselben Schlüssel wurde bereits hinzugefügt.

Ich teste das, indem ich viele Links auf der Website öffne und dann ein Chrome-Plug-in verwende, um alle Tabs zu aktualisieren und simuliere ~ 25 Anfragen, die gleichzeitig auf der Website erscheinen.

Der Dienst verfügt über zwei Methoden, die von der Route aus aufgerufen werden, und dann kann eine dieser Methoden 50 Mal aus der Controller-Aktion aufgerufen werden. Manchmal wird die Ausnahme von innerhalb der Route ausgelöst, und manchmal kommt sie aus dem Controller. Das bedeutet, dass die GetRouteData der Route abgeschlossen wurde, über den Flow an den Controller weitergeleitet wurde und dort fehlgeschlagen ist. Die Ausnahme tritt jedoch meistens innerhalb der Route auf. Wenn die Ausnahme innerhalb des Controllers auftritt, befindet sie sich in verschiedenen Zeilen, wo die Ausnahme auftritt.

Manchmal schlägt eine Methode fehl und zu einem anderen Zeitpunkt läuft diese Methode ordnungsgemäß, und die nächste Methode im Aufruf-Stack schlägt fehl. Es ist jedes Mal anders, aber diese Methodenaufrufe verwenden eine allgemeine Methode zum Abrufen aus der Datenbank.

Es gibt zwei weitere Routen, die vor dieser registriert sind und die auf * { url} abgebildet sind, die Datenbanksuchen der eingehenden URL durchführen, und die Ausnahme findet dort nie statt. Es ist also nicht so, dass diese Route die erste Operation ist, die DB-Arbeiten ausführt.

Der Dienst ist in Abhängigkeit registriert: -

%Vor%

Der Controller erhält den Service über Constructor Injection: -

%Vor%

Und die Route löst den Dienst wie folgt auf:

%Vor%

Irgendwelche Ideen, warum diese Fehler auftreten? und wie man sie löst?

Aus meiner Sicht scheint es, dass unser DBContext über zwei Anfragen verteilt wird. In meiner Abhängigkeitsregistrierung habe ich jedoch angegeben, dass er als lebenslanges Ergebnis aufgelöst werden soll - was für jede Anfrage eindeutig sein sollte. Ich habe viel über Ursachen dieser Ausnahme gelesen und wie es auf das Framework zur Abhängigkeitsinjektion ankommt, um die Lebensdauer einer Abhängigkeit zu kontrollieren und die Entsorgung ihrer Ressourcen zu verwalten - hier scheinen die Dinge herunterzufallen, scheint es.

In Bezug auf den Dienst selbst funktioniert es wie alle anderen Dienste in der Anwendung - es gibt nichts Herausragendes und Unterschiedliches.

Ich habe die vollständigen Ausnahme-Stacktraces Ссылка sowie die fehlerhafte Codezeile eingefügt.

EDIT: Ich verwende MultipleActiveResultSets = True in der Verbindungszeichenfolge. Die Entität, mit der dieser Service arbeitet, ist eigenständig, dh sie hat keine Beziehungen zu anderen Entitäten. Es sollte daher keine Mehrfachiterationen von untergeordneten Entitäten geben, nachdem die Abfrage ausgeführt wurde, wie andere Antworten in Bezug auf diese Ausnahmen zeigen. p>

EDIT: Dies ist die Zeile, die die Ausnahme auslöst.

%Vor%

Und Ausnahme ist

%Vor%

Das ist so seltsam. In meiner Route gibt es 4 Orte, an denen ein DB-Anruf stattfinden kann. Meine Ausnahmen sind 95% der Zeit von einem dieser 4 Anrufe - normalerweise schlägt der erste fehl, aber manchmal ist der erste DB Anruf in Ordnung und die anderen fallen durch. Sehr selten sehe ich die Ausnahme von innerhalb der Steuerung, dass diese Route äh ... Routen zu. Wieder, mit dieser Controller-Ausnahme passiert das eigentliche DB-Verbindungsproblem in einer von 5 Codezeilen - was wiederum zeigt, dass es viele DB-Aufrufe gibt, die dann umgefallen sind.

    
Steven Sproat 12.11.2014, 11:55
quelle

4 Antworten

2

Die letzte Lösung bestand darin, dass ich eine private Variable in meiner Route-Klasse hatte. Ich habe nicht bemerkt, dass Route-Objekte nicht bei jeder Anfrage instanziiert werden, und ich habe diese private Variable jedes Mal innerhalb von "GetRouteData" initialisiert. Bei zwei gleichzeitigen Anfragen an die Site würde daher Request A die Variable instanziieren und ihre Methoden ausführen, während Request B hereinkommt und die private Variable neu setzt, was alles für Request A durcheinander bringt.

Ich habe diese Variable jetzt lokal begrenzt und sie an andere Methoden weitergegeben, die sie benötigen, und alle meine Probleme sind weg. Also, es war nicht wirklich Autofac, noch Entity Framework im Spiel - ich hatte nicht berücksichtigt, wie MVC seine Routen behandelt.

Danke für die Hilfe und die richtige Richtung wo das Problem liegt.

    
Steven Sproat 21.11.2014, 00:01
quelle
3

Autofacs InstancePerLifetimeScope garantiert keinen eindeutigen Bereich pro HTTP-Anfrage. Was es garantiert, ist, dass es nur eine Instanz Ihrer Komponente innerhalb des Gültigkeitszeitraums geben wird, aus der sie gelöst wurde. Wenn Sie beispielsweise eine InstancePerLifetimeScope-Komponente aus dem Stammcontainer auflösen, fungiert diese Komponente im Verlauf der Anwendung im Wesentlichen als Singleton.

Wenn Sie die Lösung Ihrer Abhängigkeit innerhalb eines einzeln registrierten Dienst (zB ein globaler Aktionsfilter), dann Ihre Abhängigkeit (DbContext oder was auch immer) nicht auf jede Anfrage entsorgt bekommen - es wird nur geöffnet bleiben, undichte Speicher und blutende Verbindungen, bis entweder entsorgt wird oder die App heruntergefahren wird.

Angenommen, Sie die Autofac Mvc Integration verwenden, als ein Experiment, versuchen Sie vielleicht zu TEMPORARILY Ihre DbContext als InstancePerMatchingLifetimeScope ( „AutofacWebRequest“) registrieren - das ist eigentlich das Äquivalent genau zu tun, was Will Appleby sagte, aber ohne Affinität zu eine neuere Version von Autofac als Sie möglicherweise verwenden.

Wenn das Ihr Problem löst, dann hat @Will Appleby im Grunde recht. Sie müssen alle Komponenten Ihrer InstancePerLifetimeScope registrieren als InstancePerRequest (neu ab Version 3.4.0 zu Autofac Kern hinzugefügt) oder InstancePerHttpRequest (erhältlich in Autofac MVC Integration vor 3.4.0).

Andernfalls, wenn ich richtig rate, erhalten Sie wahrscheinlich eine Ausnahme wie folgt:

%Vor%

Wenn das passiert, werden Sie Ihre Registrierungen immer noch in InstancePerRequest / InstancePerHttpRequest ändern wollen, aber jetzt haben Sie eine tiefere Aufgabe zur Hand. Sie haben wahrscheinlich ein Singleton (oder ein anderes Objekt, das länger lebt als das httpRequest), das Ihre dbContext-Geisel hält. Sie werden feststellen, dass langlebigeren Komponente identifizieren müssen, und herauszufinden, wie diese gefangene Abhängigkeit zu befreien -., Die wahrscheinlich die Komponente benötigt Refactoring besser mit Autofac des Scoping-Verhalten zu erfüllen

Sie sollten hier auf den Abschnitt zur Fehlerbehebung verweisen: Ссылка

Hier ist ein Auszug von dort:

  

Häufige Ursachen hierfür sind:

     
  • Anwendungsregistrierungen werden für verschiedene Anwendungstypen freigegeben.
  •   
  • Ein Komponententest wird mit realen Anwendungsregistrierungen ausgeführt, simuliert jedoch nicht die Lebenszeit pro Anforderung.
  •   
  • Sie haben eine Komponente, die länger als eine Anfrage lebt, aber eine Abhängigkeit benötigt, die nur für eine Anfrage gilt. Zum Beispiel eine Singleton-Komponente, die einen Dienst als pro-Anfrage registriert.
  •   
  • Code wird während des Anwendungsstarts (z. B. in einer ASP.NET Global.asax) ausgeführt, die die Abhängigkeitsauflösung verwendet, wenn noch keine aktive Anforderung vorliegt.
  •   
  • -Code wird in einem „Hintergrund-Thread“ ausgeführt wird (wo keine Anfrage Semantik es gibt), sondern versucht, den ASP.NET MVC DependencyResolver anrufen Service Lage zu tun.
  •   
    
James Nail 20.11.2014 04:01
quelle
2

Sind Sie sicher, dass Ihrer IDbContext-Implementierung der richtige Bereich hinzugefügt wird? Ich benutze Autofac nicht selbst, aber eine schnelle Überprüfung ihrer Website-Dokumentation schlägt vor, dass InstancePerRequest ein geeigneterer Bereich für Ihre Bedürfnisse wäre:

InstanzPerRequest

Haben Sie MARS auch in Ihrer Verbindungszeichenfolge auf "True" gesetzt?

Mehrere aktive Ergebnismengen

    
Will Appleby 17.11.2014 17:18
quelle
1

Ihr Code ist im Wesentlichen korrekt. Die Abhängigkeiten, die du zeigst, stammen von nopCommerce selbst (der IDbContext ist genau derselbe, aber ich nehme an, du hast ihn ein wenig entfernt, um ihn hier zu zeigen) und deine Serviceregistrierung ist Standard. Es gibt mehr Routen mit Datenbankzugriff, die in nopcommerce nicht fehlschlagen. Es würde viele Fehlermeldungen geben, wenn das der Fall wäre.

Das brachte mich dazu zu denken, dass das Problem woanders liegen sollte, also habe ich den gleichen Test wie Sie durchgeführt, ohne irgendein Problem zu finden.

Ich habe Glimpse selbst nie benutzt, aber ich habe es in Ihrer Stack-Spur gesehen. Haben Sie den Test ohne Glimpse durchgeführt? Ich sehe, dass es Castle verwendet, um Proxies zu generieren, die mit Ihren Abhängigkeiten in Konflikt kommen könnten ... Das Problem muss irgendwo außerhalb von nopCommerce's Code liegen, vielleicht in Ihrem Code oder Tooling.

Übrigens. Ich sehe, Sie verwenden nopcommerce 3.30 oder 3.40. Seit v3.40 wird MARS nicht mehr benötigt, was zu einer Leistungssteigerung führt ...

    
Marco Regueira 18.11.2014 11:25
quelle