web-dev-qa-db-de.com

c # Arbeiten mit Entity Framework auf einem Multi-Threaded-Server

Was ist die beste Methode, um mit dem Entity Framework in einem Multi-Threaded-Server zu arbeiten? Ich benutze das Entity Framework ObjectContext, um alle meine Datenbankaktionen zu verwalten. Jetzt weiß ich, dass dieser Kontext nicht threadsicher ist Um es für einige DB-Aktionen zu verwenden, umgib ich es mit der lock-Anweisung, um sicher zu sein. Soll ich es so machen?

39
Eyal

Einige kurze Ratschläge für Entity Framework in einer Umgebung mit mehreren Threads:

  • Verwenden Sie keinen eindeutigen Kontext mit locks (kein Singleton-Muster)
  • Bereitstellen von zustandslosen Diensten (Sie müssen einen Kontext pro Anfrage instanziieren und bereitstellen).
  • Verkürzung der Kontextlebensdauer so weit wie möglich
  • Implementieren Sie ein Parallelitätskontrollsystem . Optimistische Parallelität kann problemlos mit Entity Framework ( How-to ) implementiert werden. Dadurch wird sichergestellt, dass Sie keine Änderungen in der Datenbank überschreiben, wenn Sie eine Entität verwenden, die nicht auf dem neuesten Stand ist

Ich bin etwas verwirrt, ich dachte, dass die Verwendung eines Kontexts gut ist, weil Ich glaube, es macht einen Fang, also wenn ich es mit dem selben zu tun habe. Entität in aufeinanderfolgenden Anforderungen Es ist viel schneller, dieselbe .__ zu verwenden. Kontext und erstellt dann jedes Mal einen neuen Kontext. Warum tut es gut zu Verwenden Sie es wie folgt, wenn es langsamer ist und trotzdem nicht fadensicher ist?

Sie könnten verwenden nur einen Kontext, aber es wird dringend davon abgeraten , es sei denn, Sie wissen wirklich, was Sie tun.

Ich sehe zwei Hauptprobleme, die bei einem solchen Ansatz häufig auftreten:

  1. sie benötigen viel Arbeitsspeicher, da Ihr Kontext niemals gelöscht wird und alle manipulierten Entitäten im Arbeitsspeicher zwischengespeichert werden (jede Entität, die im Ergebnis einer Abfrage angezeigt wird, wird zwischengespeichert).

  2. wenn Sie Ihre Daten aus einem anderen Programm/Kontext ändern, werden Sie mit zahlreichen Parallelitätsproblemen konfrontiert. Wenn Sie beispielsweise etwas direkt in Ihrer Datenbank ändern und die zugehörige Entität bereits in Ihrem eindeutigen Kontextobjekt zwischengespeichert wurde, weiß Ihr Kontext nie, welche Änderung direkt in der Datenbank vorgenommen wurde. Sie arbeiten mit einer zwischengespeicherten Entität, die nicht auf dem neuesten Stand ist, und vertrauen mir. Sie führt zu schwer zu findenden und zu behebenden Problemen.

Machen Sie sich auch keine Gedanken über die Leistung mehrerer Kontexte: Der Aufwand, einen neuen Kontext pro Anfrage zu erstellen/zu entsorgen, ist in 90% der Anwendungsfälle nahezu unbedeutend. Denken Sie daran, dass das Erstellen eines neuen Kontexts nicht unbedingt eine neue Verbindung zur Datenbank herstellt (da die Datenbank normalerweise einen Verbindungspool verwendet).

67
ken2k

Soll ich es so machen?

Nein. Verwenden Sie mindestens einen Kontext pro Thread, aber ich empfehle Ihnen dringend, einen Kontext als Arbeitseinheit zu betrachten und somit einen Kontext pro Arbeitseinheit pro Thread zu verwenden.

Es liegt an Ihnen, "Arbeitseinheit" für Ihre Anwendung zu definieren. Verwenden Sie nicht lock, um einen Kontext über mehrere Threads hinweg zu verwenden. Es skaliert nicht.

12
jason

Sie behandeln ObjectContext als eine extrem teure Entität. Sie werden also einmal instanziiert und dann als "Fassade" behandelt. Es besteht keine Notwendigkeit, dies zu tun. Wenn die Verbindungen aus keinem anderen Grund unter der Haube zusammengefasst sind und sehr wenig kosten (Mikrosekunde - wahrscheinlich weniger), um die "Objektkette" für die Verwendung der ObjectContext-Abstraktion vollständig einzurichten.

Der ObjectContext ist, ähnlich wie die direkte Verwendung von SqlConnection usw., so konzipiert, dass er mit einer "möglichst frühen Instanziierung und möglichst baldem Abbild" verwendet werden kann.

EF gibt Ihnen die Sicherheit, dass Sie vor dem Festlegen testen können, ob Sie über die neuesten Objekte verfügen (Optimistic Concurrency). Dies bedeutet nicht "Thread-sicher" per se, aber es bewirkt dasselbe, wenn Sie die Regeln beachten. 

5

Normalerweise sollte der ObjectContext in der gesamten Anwendung nicht global verwendet werden. Sie sollten häufig neue ObjectContexts erstellen und alte ablegen. Sie sind sicherlich auch nicht fadensicher. Wenn Sie denselben ObjectContext weiterhin verwenden (abhängig von der Lebensdauer Ihrer Anwendung), ist es leicht möglich, eine Ausnahme für den Arbeitsspeicher zu erhalten, wenn Sie große Datenmengen ändern, da Verweise auf Entitäten, die Sie ändern, im Objektkontext enthalten sind.

2
Devin

Ich erstelle für jede atomare Operation einen neuen Kontext und ordne den Kontext an. Soweit ich aus Büchern und Artikeln kenne, halte ich es, die Lebensdauer von Context so kurz wie möglich zu halten. (aber es hängt von Ihrem Ansatz und Ihrem Anwendungstyp, Winform oder Web ab)

Weitere Informationen finden Sie im großartigen Artikel . http://www.west-wind.com/weblog/posts/2008/Feb/05/Linq-to-SQL-DataContext-Lifetime-Management

Gute Bücher: http://books.google.co.th/books?id=Io7hHlVN3qQC&pg=PA580&lpg=PA580&dq=DbContext+lifetime+for+desktop+application&source=bl&ots=ogCOomQwEE&sig=At3G1Y6AbbJH7OHxgm-ZvJo0Yt8&hl=th&ei=rSlzTrjAIovOrQeD2LCuCg&sa=X&oi = book_result & ct = result & resnum = 2 & ved = 0CCgQ6AEwAQ # v = Onepage & q & f = false

Bestehende Diskussion bei Datacontext Lifetime im WinForm-Bindungsszenario

1
Pongsathon.keng

Ich verwende das Entity-Framework in einer Multithread-Umgebung, in der jeder Thread, jede Benutzeroberfläche und jeder Hintergrund (sowohl STA als auch MTA) dieselbe Datenbank gleichzeitig aktualisieren können. Ich habe dieses Problem behoben, indem ich die Entitätsverbindung zu Beginn der Verwendung in einem neuen Hintergrundthread von Grund auf neu erstellte. Bei der Untersuchung der Entitätsverbindungsinstanz ConnectionString wird eine Leser-Guid angezeigt, von der ich annehme, dass sie für die Verbindung allgemeiner Verbindungsinstanzen verwendet wird. Durch das erneute Erstellen der Entitätsverbindung von Grund auf sind die Richtwerte für jeden Thread unterschiedlich, und es scheint kein Konflikt aufzutreten. Beachten Sie, dass die Baugruppe nur die Baugruppe sein muss, in der sich das Modell befindet.

public static EntityConnection GetEntityConnection(
// Build the connection string.

  var sqlBuilder = new SqlConnectionStringBuilder();
  sqlBuilder.DataSource = serverName;
  sqlBuilder.InitialCatalog = databaseName;
  sqlBuilder.MultipleActiveResultSets = true;
  ...
  var providerString = sqlBuilder.ToString();
  var sqlConnection = new SqlConnection(providerString);

// Build the emtity connection.

  Assembly metadataAssembly = Assembly.GetExecutingAssembly();
  Assembly[] metadataAssemblies = { metadataAssembly };
  var metadataBase = @"res://*/{0}.csdl|res://*/{0}.ssdl|res://*/{0}.msl";
  var dbModelMetadata = String.Format(metadataBase, objectContextTypeModelName);
  // eg: "res://*/Models.MyDatabaseModel.csdl|res://*/Models.MyDatabaseModel.ssdl|res://*/Models.MyDatabaseModel.msl"
  var modelMetadataPaths = modelMetadata.Split('|');
  var metadataWorkspace = new MetadataWorkspace(modelMetadataPaths, metadataAssemblies);
  var entityDbConnection = new EntityConnection(metadataWorkspace, sqlConnection);
  return entityDbConnection;
0
David Coleman