2009-09-30 12 views
6

Hier ist ein kleines Experiment, das ich tat:Linq-Caching-Datenwerte - großes Nebenläufigkeitsproblem?

MyClass obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); 
Console.WriteLine(obj.MyProperty); // output = "initial" 
Console.WriteLine("Waiting..."); // put a breakpoint after this line 
obj = null; 
obj = dataContext.GetTable<MyClass>().Where(x => x.ID = 1).Single(); // same as before, but reloaded 
Console.WriteLine(obj.MyProperty); // output still = "initial" 
obj.MyOtherProperty = "foo"; 
dataContext.SubmitChanges(); // throws concurrency exception 

Wenn ich den Haltepunkt nach Zeile 3, traf ich auf ein SQL-Abfrage-Fenster gehen und den Wert manuell ändern, um „aktualisiert“. Dann renne ich weiter. Linq lädt mein Objekt nicht neu, sondern verwendet das zuvor gespeicherte Objekt erneut! Dies ist ein riesiges Problem für Daten Nebenläufigkeit!

Wie bewerten Sie diesen versteckten Cache von Objekten deaktivieren, die Linq offensichtlich im Speicher zu halten?

EDIT - Bei näherer Betrachtung ist es einfach undenkbar, dass Microsoft eine solche klaffenden Abgrund im Linq Rahmen verlassen haben könnte. Der obige Code ist eine verdummte Version dessen, was ich gerade tue, und vielleicht gibt es kleine Feinheiten, die ich übersehen habe. Kurz gesagt, würde ich mich freuen, wenn Sie Ihre eigenen Experimente durchführen würden, um zu bestätigen, dass meine obigen Ergebnisse richtig sind. Alternativ muss es eine Art "geheimer Schalter" geben, der Linq gegen gleichzeitige Datenaktualisierungen robust macht. Aber was? Diese

Antwort

5

ist kein Problem, ich über vor gekommen sind (da ich neigen dazu, nicht Datacontexts offen zu halten für längere Zeit), aber es sieht aus wie jemand anderes hat:

http://www.rocksthoughts.com/blog/archive/2008/01/14/linq-to-sql-caching-gotcha.aspx

+1

+1 OMG, es ist wirklich ein Gotcha! Muss das hinzufügen, um die C# Gotcha KB ... –

+1

Schlimmer noch, wenn Sie seinen Rat dort befolgen und ObjectTrackingEnabled = false setzen, können Sie nur aus der DB lesen; Sie können SubmitChanges nicht tun! Das macht das Leben wirklich schwierig! –

+0

Hier ist ein Link zu der C# Gotcha KB: http://StackOverflow.com/Questions/241134/Bat-ist-the-Worst-C-Net-Goecha –

2

Setzen Sie die ObjectTrackingEnabled-Eigenschaft des DataContext auf false.

Wenn ObjectTrackingEnabled auf true Datacontext gesetzt ist, wie ein Unit of Work verhält. Es wird jedes Objekt im Speicher behalten, damit es Änderungen daran verfolgen kann. Der DataContext muss sich das Objekt so merken, wie Sie es ursprünglich geladen haben, um zu wissen, ob Änderungen vorgenommen wurden.

Wenn Sie in einem schreibgeschützten Szenario arbeiten, sollten Sie die Objektverfolgung deaktivieren. Es kann eine anständige Leistungsverbesserung sein.

Wenn Sie in einem Nur-Lese-Szenario nicht berufstätig sind, dann bin ich nicht sicher, warum Sie es wollen auf diese Weise arbeiten. Wenn Sie Änderungen vorgenommen haben, warum sollten Sie dann den geänderten Status aus der Datenbank abrufen wollen?

+0

Ja, aber ich arbeite nicht in einer schreibgeschützten Umgebung. Irgendwelche anderen Ideen? –

+0

Dies ist mit Absicht. Es wurde entwickelt, damit Sie den Status abrufen, ändern und das Zurückschreiben mit optamistischer Parallelität durchführen können. Sie könnten Refresh für den DataContext aufrufen, aber dadurch könnten Sie alle Änderungen rückgängig machen. Es ist nicht vorgesehen, eine Echtzeit-Synchronisierung bereitzustellen. Echtzeit-Synchronisierung und Bearbeitung passen nicht gut zusammen. –

1

LINQ to SQL verwendet die Identitätskarte Design-Muster, das bedeutet, dass es immer die gleiche Instanz eines Objekts zurückgeben für sie Primärschlüssel gegeben ist (es sei denn, Sie Objektverfolgung deaktivieren).

Die Lösung einfach entweder einen zweiten Datenkontext verwenden, wenn Sie es mit der ersten Instanz oder aktualisieren Sie die erste Instanz nicht stören wollen, wenn Sie das tun.

5

LinqToSql verfügt über eine Vielzahl von Tools zur Behandlung von Problemen mit Nebenläufigkeit.

Der erste Schritt ist jedoch zugeben, ein Concurrency Problem gelöst werden soll!

Zuerst soll der beabsichtigte Objektlebenszyklus von DataContext mit einem UnitOfWork übereinstimmen. Wenn Sie sich für längere Zeit an einem halten, müssen Sie viel härter arbeiten, weil die Klasse nicht dafür ausgelegt ist.

Zweitens verfolgt DataContext zwei Kopien jedes Objekts. Einer ist der ursprüngliche Zustand und einer ist der veränderte/veränderbare Zustand. Wenn Sie nach der MyClass mit Id = 1 fragen, gibt es Ihnen die gleiche Instanz es gab Ihnen das letzte Mal, das ist die geänderte/änderbare Version ... nicht das Original.Es muss dies tun, um Parallelitätsprobleme mit Speicherinstanzen zu verhindern ... LinqToSql lässt nicht zu, dass ein DataContext zwei änderbare Versionen von MyClass (Id = 1) kennt.

Drittens hat DataContext keine Ahnung, ob Ihre In-Memory-Änderung vor oder nach der Datenbankänderung erfolgt, und kann daher den Concurrency-Konflikt nicht ohne eine Anleitung steuern. Alles was es sieht ist:

  • Ich las MyClass (Id = 1) aus der Datenbank.
  • Programmierer modifizierte MyClass (Id = 1).
  • I MyClass (Id = 1) zurück an die Datenbank gesendet (an dieser SQL aussehen Parallelität in der where-Klausel, um zu sehen)
    • Das Update wird erfolgreich sein, wenn die Version der ursprünglichen (Parallelität) entspricht der Datenbank.
    • Das Update schlägt mit einer Ausnahme für den gemeinsamen Zugriff fehl, wenn die Version der Datenbank nicht mit dem Original übereinstimmt.

Ok, jetzt, dass das Problem, hier angegeben ist ein paar Möglichkeiten, damit umzugehen.

Sie können den DataContext wegwerfen und von vorne beginnen. Das ist etwas schwer für einige, aber zumindest ist es einfach zu implementieren.

Sie können anfordern, dass die ursprüngliche Instanz oder die geänderte/änderbare Instanz mit dem Datenbankwert aktualisiert wird, indem Sie DataContext.Refresh(RefreshMode, target) (reference docs with many good concurrency links in the "Remarks" section) aufrufen. Dies bringt die Änderungen auf die Client-Seite und ermöglicht es Ihrem Code herauszufinden, was das Endergebnis sein soll.

Sie können die Parallelitätsprüfung in der dbml (ColumnAttribute.UpdateCheck) deaktivieren. Dadurch wird die optimistische Nebenläufigkeit deaktiviert und der Code stampft über die Änderungen anderer Personen hinweg. Auch schwerfällig, auch einfach zu implementieren.