Dzięki EF6 masz nową transakcję, której możesz użyć na przykład:
using (var context = new PostEntityContainer())
{
using (var dbcxtransaction = context.Database.BeginTransaction())
{
try
{
PostInformation NewPost = new PostInformation()
{
PostId = 101,
Content = "This is my first Post related to Entity Model",
Title = "Transaction in EF 6 beta"
};
context.Post_Details.Add(NewPost);
context.SaveChanges();
PostAdditionalInformation PostInformation = new PostAdditionalInformation()
{
PostId = (101),
PostName = "Working With Transaction in Entity Model 6 Beta Version"
};
context.PostAddtional_Details.Add(PostInformation);
context.SaveChanges();
dbcxtransaction.Commit();
}
catch
{
dbcxtransaction.Rollback();
}
}
}
Czy wycofywanie zmian jest rzeczywiście potrzebne, gdy sprawy idą bokiem? Jestem zaciekawiony, ponieważ opis zatwierdzenia mówi: „Zatwierdza podstawową transakcję sklepu”.
Natomiast opis wycofania mówi: „Wycofuje transakcję w sklepie bazowym”.
To mnie zaciekawia, ponieważ wydaje mi się, że jeśli Commit nie zostanie wywołane, wcześniej wykonane polecenia nie zostaną zapisane (co wydaje mi się logiczne). Ale jeśli tak jest, jaki byłby powód wywołania funkcji wycofywania zmian? W EF5 użyłem TransactionScope, który nie miał funkcji Rollback (tylko Complete), co wydawało mi się logiczne. Z powodów związanych z MS DTC nie mogę już korzystać z TransactionScope, ale nie mogę również używać try catch, jak na powyższym przykładzie (tj. Potrzebuję tylko Commit).
źródło
SaveChanges
tylko raz).Odpowiedzi:
Nie musisz dzwonić
Rollback
ręcznie, ponieważ używaszusing
instrukcji.DbContextTransaction.Dispose
zostanie wywołana na końcuusing
bloku. I automatycznie wycofa transakcję, jeśli transakcja nie zostanie pomyślnie zatwierdzona (nie wywołano lub nie napotkano wyjątków). Poniżej znajduje się kod źródłowySqlInternalTransaction.Dispose
metody (DbContextTransaction.Dispose
ostatecznie deleguje do niej przy użyciu dostawcy SqlServer):private void Dispose(bool disposing) { // ... if (disposing && this._innerConnection != null) { this._disposing = true; this.Rollback(); } }
Widzisz, sprawdza, czy
_innerConnection
nie jest null, jeśli nie, wycofuje transakcję (jeśli została zatwierdzona,_innerConnection
będzie zerowa). Zobaczmy, coCommit
robi:internal void Commit() { // Ignore many details here... this._innerConnection.ExecuteTransaction(...); if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer) { // Zombie() method will set _innerConnection to null this.Zombie(); } else { this.ZombieParent(); } // Ignore many details here... } internal void Zombie() { this.ZombieParent(); SqlInternalConnection innerConnection = this._innerConnection; // Set the _innerConnection to null this._innerConnection = null; if (innerConnection != null) { innerConnection.DisconnectTransaction(this); } }
źródło
Dopóki zawsze będziesz używać SQL Server z EF, nie ma potrzeby jawnego używania catch, aby wywołać metodę Rollback. Zezwolenie blokowi using na automatyczne wycofywanie wszystkich wyjątków zawsze będzie działać.
Jednak gdy myślisz o tym z punktu widzenia Entity Framework, możesz zobaczyć, dlaczego wszystkie przykłady używają jawnego wywołania wycofania transakcji. W przypadku EF dostawca bazy danych jest dowolny i można go podłączyć, a dostawcę można zastąpić MySQL lub dowolną inną bazą danych, która ma implementację dostawcy EF. Dlatego z punktu widzenia EF nie ma gwarancji, że dostawca automatycznie wycofa usuniętą transakcję, ponieważ EF nie wie o implementacji dostawcy bazy danych.
Dlatego zgodnie z najlepszą praktyką dokumentacja EF zaleca jawne wycofywanie zmian - na wypadek, gdyby pewnego dnia zmieniono dostawców na implementację, która nie jest automatycznie przywracana przy usuwaniu.
Moim zdaniem każdy dobry i dobrze napisany dostawca automatycznie wycofa transakcję w dyspozycji, więc dodatkowy wysiłek, aby zawinąć wszystko w bloku using za pomocą try-catch-rollback, jest przesadą.
źródło
źródło