0

Ich benutze EF Core (in ASP.NET Core und meine DbContext Leben pro Anfrage/Bereich).Ändern der eingeschränkten Spalte in EF Core mit einer Transaktion

Meine Employee Einheit hat diese Eigenschaft:

public bool? IsBoss { get; set; } 

und diese config:

entityBuilder.Property(b => b.IsBoss).IsRequired(false); 
entityBuilder.HasIndex(b => b.IsBoss).IsUnique(); 

Dies erzeugt einen gefilterten Index, so kann es nur eine wahre, eine falsche, aber viele Nullen sein.

Meine App erfordert, dass ich immer genau einen Mitarbeiter mit IsBoss==true habe.

Angenommen, ich möchte zwei Mitarbeiter tauschen.

employee1.IsBoss = null; 
employee2.IsBoss = true; 
context.SaveChanges(); 

Dies löst eine Ausnahme für eine Ausnahme für bedingte Verletzung aus.

Ich kann das Problem beheben, indem sie in einer Transaktion Verpackung:

using (var transaction = context.BeginTransaction()) 
{ 
    try 
    { 
    employee1.IsBoss = null; 
    context.SaveChanges(); 

    employee2.IsBoss = true; 
    context.SaveChanges(); 

    transaction.Commit(); 
    } 
    catch 
    { 
    transaction.Rollback(); 
    } 
} 

Meine Frage ist: Warum scheitern wird der erste Ansatz? Ich dachte EF Core automatically wraps alles in einer Transaktion. Warum muss ich eine Transaktion verwenden?

Antwort

1

Der erste Ansatz schlägt aus anderen Gründen als Transaktionen fehl. Tracking issue auf EF Repo, die das gleiche Szenario wie Ihres umfasst.

Wenn SaveChanges aufgerufen wird, verarbeitet EF die Änderungen und berechnet Befehle, die an die Datenbank gesendet werden sollen. Dieser Befehlssatz kann eine Abhängigkeit haben. Wie in Ihrem Fall müssen Sie den Wert null für employee1 festlegen, bevor Sie den Wert true für employee2 festlegen können. EF sortiert Befehle, um die Reihenfolge herauszufinden, in der sie ausgeführt werden müssen. EF hat diese Sortierung basierend auf den Fremdschlüsseleinschränkungen durchgeführt. Aber als dieses Problem gemeldet wurde, haben wir keinen eindeutigen Index berücksichtigt, sodass Befehle in falscher Reihenfolge gesendet wurden, was zu einer Verletzung der eindeutigen Einschränkung führte.

Das Problem ist bereits in der aktuellen Codebasis behoben. Es wird in der nächsten öffentlichen Version verfügbar sein. In der Zwischenzeit müssen Sie die Nummer SaveChanges zweimal aufrufen, wie Sie es in Ihrem zweiten Code tun. Wenn Sie mehrere Male SaveChanges aufrufen, können Sie die Reihenfolge der Befehle steuern, die an die Datenbank gesendet werden. Sie müssen es nicht in eine Transaktion einbinden, es sei denn, Sie möchten, dass beide Änderungen eine atomare Operation sind. Jede SaveChanges hat eine eigene Transaktion, sofern der Benutzer keine startet.

+0

Danke für die detaillierte Antwort ... Ich habe nicht erwartet, dass es ein Fehler ist! Willst du sagen, dass die nächste Version mir erlauben wird, dies zu tun 1) mit nur einem 'SaveChanges()' und 2) ohne eine Transaktion? – grokky

+1

Antwort ist ja für beide Fragen :) Sie werden nur einen 'SaveChanges' brauchen und standardmäßig wird es in die Transaktion eingebunden. – Smit