Transaktionen verwenden oder Savechanges (false) und AcceptAllChanges ()?
-
03-07-2019 - |
Frage
Ich habe untersucht worden Transaktionen und es scheint, dass sie sich selbst in EF kümmern, solange ich false
passieren SaveChanges()
und rufen AcceptAllChanges()
dann, wenn es keine Fehler sind:
SaveChanges(false);
// ...
AcceptAllChanges();
Was ist, wenn etwas schlecht geht? nicht muss ich oder Rollback, sobald meine Methode den Gültigkeitsbereich verlässt, ist die Transaktion beendet?
Was passiert mit irgendwelchen indentiy Spalten, die auf halbem Weg durch die Transaktion zugewiesen wurden? Ich nehme an, wenn jemand anders einen Rekord nach meinem hinzugefügt, bevor Mine schlecht ging dann das bedeutet, es wird eine fehlende Identität Wert sein.
Gibt es einen Grund, den Standard TransactionScope
Klasse in meinem Code zu benutzen?
Lösung
Mit dem Entity Framework die meiste Zeit SaveChanges()
ausreichend. Dies schafft eine Transaktion oder anwirbt in jeder Ambient-Transaktion und macht alle, die notwendigen Arbeiten in dieser Transaktion.
Manchmal aber die SaveChanges(false) + AcceptAllChanges()
Paarung nützlich ist.
Der nützlichste Ort dafür ist, in Situationen, in denen Sie eine verteilte Transaktion über zwei verschiedene Kontexte tun wollen.
d. so etwas wie diese (sehr schlecht):
using (TransactionScope scope = new TransactionScope())
{
//Do something with context1
//Do something with context2
//Save and discard changes
context1.SaveChanges();
//Save and discard changes
context2.SaveChanges();
//if we get here things are looking good.
scope.Complete();
}
Wenn context1.SaveChanges()
gelingt aber context2.SaveChanges()
nicht die gesamte verteilte Transaktion abgebrochen. Aber leider hat das Entity Framework verworfen bereits die Änderungen auf context1
, so kann man nicht wiederholen oder effektiv die Fehler protokolliert.
Aber wenn Sie Ihren Code ändern wie folgt aussehen:
using (TransactionScope scope = new TransactionScope())
{
//Do something with context1
//Do something with context2
//Save Changes but don't discard yet
context1.SaveChanges(false);
//Save Changes but don't discard yet
context2.SaveChanges(false);
//if we get here things are looking good.
scope.Complete();
context1.AcceptAllChanges();
context2.AcceptAllChanges();
}
Während der Aufruf an SaveChanges(false)
die notwendigen Befehle an die Datenbank sendet, sich der Kontext nicht geändert wird, so dass Sie es wieder tun können, wenn nötig, oder Sie können die ObjectStateManager
abfragen, wenn Sie wollen.
Das bedeutet, wenn die Transaktion tatsächlich eine Ausnahme auslöst können Sie, entweder durch erneutes versuchen oder Protokollierung Zustand jeder Kontexte kompensieren ObjectStateManager
irgendwo.
Andere Tipps
Wenn Sie mit EF6 (Entity Framework 6+), hat sich dies geändert für Datenbank SQL-Aufrufe.
Siehe: http://msdn.microsoft.com/en-us/data/dn456843.aspx
context.Database.BeginTransaction verwenden.
Von MSDN:
using (var context = new BloggingContext()) { using (var dbContextTransaction = context.Database.BeginTransaction()) { try { context.Database.ExecuteSqlCommand( @"UPDATE Blogs SET Rating = 5" + " WHERE Name LIKE '%Entity Framework%'" ); var query = context.Posts.Where(p => p.Blog.Rating >= 5); foreach (var post in query) { post.Title += "[Cool Blog]"; } context.SaveChanges(); dbContextTransaction.Commit(); } catch (Exception) { dbContextTransaction.Rollback(); //Required according to MSDN article throw; //Not in MSDN article, but recommended so the exception still bubbles up } } }
Da einige Datenbank kann eine Ausnahme bei dbContextTransaction.Commit werfen () so besser dies:
using (var context = new BloggingContext())
{
using (var dbContextTransaction = context.Database.BeginTransaction())
{
try
{
context.Database.ExecuteSqlCommand(
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'"
);
var query = context.Posts.Where(p => p.Blog.Rating >= 5);
foreach (var post in query)
{
post.Title += "[Cool Blog]";
}
context.SaveChanges(false);
dbContextTransaction.Commit();
context.AcceptAllChanges();
}
catch (Exception)
{
dbContextTransaction.Rollback();
}
}
}