Datenzugriffskonflikte bei Entity Framework Core
20.07.2017, 00:00 Uhr
Datenzugriffskonflikte bei Entity Framework Core: Der Schlichter
Es gibt mehrere Lösungen, um Laufzeitfehler bei Änderungskonflikten zu vermeiden.
In der letzten Ausgabe dieser Kolumne ging es darum, Entity Framework (EF) Core so einzustellen, dass es Konflikte bei Datenänderungen durch verschiedene Prozesse erkennt. Das Erkennen ist nur der eine Teil, danach muss eine Anwendung mit einem solchen Konflikt auch noch zum Wohle des Anwenders umgehen [1].
Listing 1 (Teil 1): Konflikt erkennen und lösen mit Entity Framework Core
public static void EF_Konflikt_BeiFlug()
{
CUI.MainHeadline(nameof(EF_Konflikt_BeiFlug));
Console.WriteLine("Prozess.ID=" + System.Diagnostics.Process.GetCurrentProcess().Id);
Console.Title = nameof(EF_Konflikt_BeiFlug) + ": Prozess-ID=" + System.Diagnostics.Process.GetCurrentProcess().Id;
int flugNr = 151;
// Kontext instanzieren
using (WWWingsContext ctx = new WWWingsContext())
{
// --- Flug laden
Flug flug = ctx.FlugSet.SingleOrDefault(x => x.FlugNr == flugNr);
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze VORHER: " + flug.FreiePlaetze);
short zuBuchendePlaetze = 0;
string eingabe = "";
do
{
Console.WriteLine("Wieviele Plätze?");
eingabe = Console.ReadLine(); // Warten (zum Starten eines zweiten Prozesses!)
} while (!short.TryParse(eingabe, out zuBuchendePlaetze));
// --- Änderung
flug.FreiePlaetze -= zuBuchendePlaetze;
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze NEU: " + flug.FreiePlaetze);
try
{
// --- Speicherversuch
ITVisions.EFCore.EFC_Util.PrintChangedProperties(ctx.Entry(flug));
var anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
}
catch (DbUpdateConcurrencyException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
CUI.PrintError(DateTime.Now.ToLongTimeString() + ": Fehler: Ein anderer Benutzer hat bereits den Datensatz geändert!");
CUI.Print("Konflikte bei folgenden Eigenschaften:");
ITVisions.EFCore.EFC_Util.PrintChangedProperties(ex.Entries.Single());
// --- Nachfrage beim Nutzer
Console.WriteLine("Was möchten Sie tun?");
Console.WriteLine("Taste 1: Werte des anderen Benutzer übernehmen");
Console.WriteLine("Taste 2: Werte des anderen Benutzer überschreiben");
Console.WriteLine("Taste 3: Werte für ‚FreiePlaetze‘ verrechnen");
ConsoleKeyInfo key = Console.ReadKey();
switch(key.Key)
{
case ConsoleKey.D1: // Werte des anderen Nutzers übernehmen
{
Console.WriteLine("Sie haben gewählt: Option 1: übernehmen");
ctx.Entry(flug).Reload();
break;
}
case ConsoleKey.D2: // Werte des anderen Nutzers überschreiben
{
Console.WriteLine("Sie haben gewählt: Option 2: überschreiben");
// Werte des anderen Nutzers überschreiben
ctx.Entry(flug).OriginalValues.SetValues(ctx.Entry(flug).GetDatabaseValues());
// wie RefreshMode.ClientWins bei ObjectContext
ITVisions.EFCore.EFC_Util.PrintChangeInfo(ctx);
int anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
break;
}
case ConsoleKey.D3: // Werte des anderen Benutzers überschreiben
{
Console.WriteLine("Sie haben gewählt: Option 3: verrechnen");
var freiePlaetzeOrginal = ctx.Entry(flug).OriginalValues.GetValue<short?>("FreiePlaetze");
var freiePlaetzeNun = flug.FreiePlaetze.Value;
var freiePlaetzeInDB = ctx.Entry(flug).GetDatabaseValues().GetValue<short?>("FreiePlaetze");
flug.FreiePlaetze = (short) (freiePlaetzeOrginal - (freiePlaetzeOrginal - freiePlaetzeNun) - (freiePlaetzeOrginal - freiePlaetzeInDB));
ITVisions.EFCore.EFC_Util.PrintChangeInfo(ctx);
ctx.Entry(flug).OriginalValues.SetValues(ctx.Entry(flug).GetDatabaseValues());
int anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
break;
}
}
}
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze NACHHER: " + flug.FreiePlaetze);
// --- Gegenprobe des nun gültigen Zustandes
using (WWWingsContext ctx2 = new WWWingsContext())
{
var f = ctx.FlugSet.Where(x => x.FlugNr == flugNr).SingleOrDefault();
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze GEGENPROBE: " + f.FreiePlaetze);
} // Ende using-Block -> Dispose() wird aufgerufen
}
}
{
CUI.MainHeadline(nameof(EF_Konflikt_BeiFlug));
Console.WriteLine("Prozess.ID=" + System.Diagnostics.Process.GetCurrentProcess().Id);
Console.Title = nameof(EF_Konflikt_BeiFlug) + ": Prozess-ID=" + System.Diagnostics.Process.GetCurrentProcess().Id;
int flugNr = 151;
// Kontext instanzieren
using (WWWingsContext ctx = new WWWingsContext())
{
// --- Flug laden
Flug flug = ctx.FlugSet.SingleOrDefault(x => x.FlugNr == flugNr);
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze VORHER: " + flug.FreiePlaetze);
short zuBuchendePlaetze = 0;
string eingabe = "";
do
{
Console.WriteLine("Wieviele Plätze?");
eingabe = Console.ReadLine(); // Warten (zum Starten eines zweiten Prozesses!)
} while (!short.TryParse(eingabe, out zuBuchendePlaetze));
// --- Änderung
flug.FreiePlaetze -= zuBuchendePlaetze;
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze NEU: " + flug.FreiePlaetze);
try
{
// --- Speicherversuch
ITVisions.EFCore.EFC_Util.PrintChangedProperties(ctx.Entry(flug));
var anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
}
catch (DbUpdateConcurrencyException ex)
{
Console.ForegroundColor = ConsoleColor.Red;
CUI.PrintError(DateTime.Now.ToLongTimeString() + ": Fehler: Ein anderer Benutzer hat bereits den Datensatz geändert!");
CUI.Print("Konflikte bei folgenden Eigenschaften:");
ITVisions.EFCore.EFC_Util.PrintChangedProperties(ex.Entries.Single());
// --- Nachfrage beim Nutzer
Console.WriteLine("Was möchten Sie tun?");
Console.WriteLine("Taste 1: Werte des anderen Benutzer übernehmen");
Console.WriteLine("Taste 2: Werte des anderen Benutzer überschreiben");
Console.WriteLine("Taste 3: Werte für ‚FreiePlaetze‘ verrechnen");
ConsoleKeyInfo key = Console.ReadKey();
switch(key.Key)
{
case ConsoleKey.D1: // Werte des anderen Nutzers übernehmen
{
Console.WriteLine("Sie haben gewählt: Option 1: übernehmen");
ctx.Entry(flug).Reload();
break;
}
case ConsoleKey.D2: // Werte des anderen Nutzers überschreiben
{
Console.WriteLine("Sie haben gewählt: Option 2: überschreiben");
// Werte des anderen Nutzers überschreiben
ctx.Entry(flug).OriginalValues.SetValues(ctx.Entry(flug).GetDatabaseValues());
// wie RefreshMode.ClientWins bei ObjectContext
ITVisions.EFCore.EFC_Util.PrintChangeInfo(ctx);
int anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
break;
}
case ConsoleKey.D3: // Werte des anderen Benutzers überschreiben
{
Console.WriteLine("Sie haben gewählt: Option 3: verrechnen");
var freiePlaetzeOrginal = ctx.Entry(flug).OriginalValues.GetValue<short?>("FreiePlaetze");
var freiePlaetzeNun = flug.FreiePlaetze.Value;
var freiePlaetzeInDB = ctx.Entry(flug).GetDatabaseValues().GetValue<short?>("FreiePlaetze");
flug.FreiePlaetze = (short) (freiePlaetzeOrginal - (freiePlaetzeOrginal - freiePlaetzeNun) - (freiePlaetzeOrginal - freiePlaetzeInDB));
ITVisions.EFCore.EFC_Util.PrintChangeInfo(ctx);
ctx.Entry(flug).OriginalValues.SetValues(ctx.Entry(flug).GetDatabaseValues());
int anz = ctx.SaveChanges();
Console.WriteLine("SaveChanges: Gespeicherte Änderungen: " + anz);
break;
}
}
}
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze NACHHER: " + flug.FreiePlaetze);
// --- Gegenprobe des nun gültigen Zustandes
using (WWWingsContext ctx2 = new WWWingsContext())
{
var f = ctx.FlugSet.Where(x => x.FlugNr == flugNr).SingleOrDefault();
Console.WriteLine(DateTime.Now.ToLongTimeString() + ": Freie Plätze GEGENPROBE: " + f.FreiePlaetze);
} // Ende using-Block -> Dispose() wird aufgerufen
}
}
Jetzt 1 Monat kostenlos testen!
Sie wollen zukünftig auch von den Vorteilen eines plus-Abos profitieren? Werden Sie jetzt dotnetpro-plus-Kunde.
- + Digitales Kundenkonto,
- + Zugriff auf das digitale Heft,
- + Zugang zum digitalen Heftarchiv,
- + Auf Wunsch: Weekly Newsletter,
- + Sämtliche Codebeispiele im digitalen Heftarchiv verfügbar