2012-03-25 12 views
-2

Meine Frage geht von einfachen Einfügungen in eine Tabelle ohne relevante Beziehungen aus.Setzt DbContext.SaveChanges() neue Datensätze in der Reihenfolge ein, in der sie zu DbSet hinzugefügt wurden?

// various unrelated operations w/context... 
var one = new DbRecord(); 
var two = new DbRecord(); 
var thr = new DbRecord(); 
context.DbRecords.Add(one); 
context.DbRecords.Add(two); 
context.DbRecords.Add(thr); 
// various unrelated operations w/context... 
context.SaveChanges(); 

In diesem Fall werden meine DbRecord Einheiten immer in der Reihenfolge eingefügt ich sich den DbSet hinzugefügt? Sie scheinen in meinen Tests zu sein, aber kann ich mich darauf verlassen?

"verschiedene nicht verwandte Operationen" bezieht sich auf Operationen auf verschiedenen, nicht verwandten DbSets des gleichen Kontextes; Einfügungen, Löschungen und aktualisierte Entitäten (POCOs in meinem Fall)

Ich möchte, dass sie in exakter Reihenfolge eingefügt werden, so dass ich das PK/Identity-Feld zum Sortieren verwenden kann, aber ich muss auch die implizite nutzen Transaktion, die der Kontext um mein context.SaveChanges() bereitstellt. Obwohl die anderen Operationen nicht in Bezug auf das Datenbankschema verwandt sind, sind die Einträge selbst im Wesentlichen Protokolleinträge über die ausgeführten Aktualisierungen und ihre Reihenfolge ist kritisch.

Wenn der Kontext nicht garantiert, dass die Datensätze in der gleichen Reihenfolge eingefügt werden, muss ich den Datensätzen ein Datetime-Feld hinzufügen und das Zurückrollen selbst übernehmen.

+0

Warum? In der Datenbank selbst gibt es keine "Reihenfolge der Einfügung". Verwenden Sie in den Abfragen eine ORDER BY-Anweisung. –

+0

@Henk Ich werde wahrscheinlich ein Datetime-Feld verwenden und danach sortieren. Ich versuche zu sehen, ob ich nach dem Feld identity/pk sortieren kann. –

+0

@Henk: Natürlich hat eine Datenbank das Konzept der Reihenfolge der Insertion. Wenn Sie eine Identitätsspalte haben, ist der Wert dieser Spalte im Wesentlichen die Reihenfolge der Einfügung. Sie können dann nach dieser Spalte sortieren und die Datensätze in der Reihenfolge des Einfügens zurückerhalten. –

Antwort

4

Dies ist nicht garantiert gemäß der Dokumentation. Dies bedeutet, dass Sie sich nicht auf dieses Verhalten verlassen können. Grundsätzlich kommt es auf die Fragen an, ob Sie bereit sind, das Risiko einzugehen, dass ...

  • ... Ihr Pager geht mitten in der Nacht?
  • ... finden Sie, nach einem Jahr Laufzeit, dass 10% dieser Datensätze beschädigt sind, ohne dass sie rückwirkend repariert werden können?

Die Beantwortung dieser Fragen bleibt Ihnen überlassen. Normalerweise sollte die Antwort "Nein" sein.

Wenn Ihre Tests in Ordnung sind, bedeutet das nicht, dass die "nicht verwandten Aktionen", über die Sie sprachen, in seltenen Situationen keine Störungen verursachen. Dies ist ein stiller Fehler, den niemand beim Testen bemerkt.

+1

Das war genau mein Anliegen. Ich habe die Datenbank ursprünglich so geplant, dass ich Datensätze außerhalb des Contexts zur Tabelle hinzufüge, aber es wird viel einfacher, den Kontext zu verwenden, um mir dabei zu helfen. Es sieht also so aus, als wäre ein Datetime-Feld in Ordnung! +1 –

+0

Natürlich können Sie SaveChanges auch nach jedem hinzugefügten Protokolldatensatz ausführen (und alles in eine Transaktion umbrechen, so dass es immer noch atomar ist). Oder Sie könnten sofort mit ExecuteCommand einfügen. – usr

+0

Ich kann vom Testen bestätigen, dass Elemente, die in einer bestimmten Reihenfolge hinzugefügt wurden, nicht in dieser Reihenfolge gespeichert wurden, als ich SaveChanges() ausgeführt habe. Bestellung kann nicht garantiert werden. Ich habe nach jedem Einfügen einen Speichervorgang durchgeführt und jetzt verhält es sich wie erwartet (aber offensichtlich langsamer). – JackMorrissey

1

Ich nehme an, dass es nicht in der gleichen Reihenfolge wie hinzugefügt eingefügt wird, da ich keine verwandten Informationen finden kann, die dies bestätigen. Auf der anderen Seite glaube ich, dass dies helfen könnte, zumindest hoffe ich es. =)

How to observe the Add action of DbSet?

Obwohl ein Datetime-Feld Hinzufügen einfacher sein könnte, sieht dieser Ansatz interessant für das, was Sie scheinen tun zu wollen. Es schlägt vor, dass Sie die Add-Aktion abonnieren und die Reihenfolge für sich selbst festlegen, so dass Sie sicherstellen können, dass es eingefügt wird, wie Sie es möchten.

Hoffe, das hilft! =)

+0

erhalten Das könnte sich wirklich sehen lassen. Obwohl ein Datetime-Feld für meine Situation einfacher sein könnte. +1 für das "nicht annehmen, da es nicht so sagt" und die Idee! –

0

Wenn Sie nur Zeilen hinzufügen, können Sie den PK inkrementell haben. In diesem Fall können Sie darauf vertrauen, dass diese in der richtigen Reihenfolge sind. Wenn Sie wirklich die Zeit der letzten Bearbeitung möchten, sollten Sie jedoch eine datetime-Spalte verwenden, wie Sie gesagt haben. Aber alles hört sich viel einfacher und sauber an, wenn Sie das eigentliche Problem, mit dem Sie es zu tun haben, näher erläutern wollen.

+0

Sie haben Recht; Ich werde den Weg des Hinzufügens eines Datetime-Felds gehen, um eine ordnungsgemäße Sortierung sicherzustellen. Obwohl ich inkrementelle pk-Werte zugewiesen habe, ist das Problem, ich glaube nicht, dass ich mich darauf verlassen kann, dass EF die Datensätze in der gleichen Reihenfolge einfügt, wie ich es ihnen erzählt habe. –

+0

Incentive pk's werden genau richtig vergeben, versprechen. Edit: könnte von der db abhängen? Was verwendest du für db? – SimpleVar

+0

Ich weiß, dass inkrementelle PKs ordnungsgemäß zugewiesen werden, basierend auf der Reihenfolge, in der die SQL INSERT-Anweisungen definitiv kommen. Was nicht garantiert ist, ist, dass Entity Framework diese INSERT-Anweisungen in der Reihenfolge ausgibt, die ich DbSet.Add() nannte; –

1

Ist es in Ihrem Fall nicht möglich, eine explizite Übertragung zu verwenden?Ich meine, ich würde etwas tun wie:

using (TransactionScope transaction = new TransactionScope()){ 
    var one = new DbRecord(); 
    var two = new DbRecord(); 
    var thr = new DbRecord(); 
    context.DbRecords.Add(one); 
    context.SaveChanges(); 
    context.DbRecords.Add(two); 
    context.SaveChanges(); 
    context.DbRecords.Add(thr); 
    context.SaveChanges(); 
    // various unrelated operations w/context... 
    context.SaveChanges(); 
    transaction.Complete(); 
} 
+0

Das wäre möglich, ja ... aber es wäre viel komplizierter, als einfach ein Datetime-Feld zur Tabelle hinzuzufügen. Die Sache ist, ich würde einige dieser verschiedenen anderen Operationen in die spezifischen Transaktionen einbeziehen müssen, was einen viel komplizierteren Transaktionscode bedeuten würde. –

+0

Ich glaube nicht, dass Sie ein zusätzliches Datetime-Feld mit einer ähnlichen Lösung benötigen würden. Tatsächlich ist der Zweck einer expliziten Transaktion wie dieser, dass die Reihenfolge der Identitäten garantiert ist. Ich verstehe nicht, warum Sie einen komplizierten Transaktionscode benötigen. Ich sehe keinen Unterschied zwischen (TransactionScope transaction = new TransactionScope()) {doSumeDbRelatedStuff1(); context.SaveChanges(); doSumeDbRelatedStuff2(); context.SaveChanges(); transaction.Complete(); } und die andere Version ohne explizite Transaktion: doSumeDbRelatedStuff1(); doSumeDbRelatedStuff2(); context.SaveChanges(); – Hari

+0

Ich habe deinen Vorschlag zunächst missverstanden. Der Transaktionscode wäre eigentlich nicht kompliziert, aber der Update-Code wäre ein bisschen. Ich nehme an, ich könnte einfach die Lebensdauer des Context in eine Transaktion umwandeln und den Aufruf der 'Save()' Methode meines Repositorys dazu bringen, es zu committen. –

Verwandte Themen