Sometimes applications have to perform multiple operations on the database. Traditionally, database level transactions are used by applications in order to ensure atomicity of such operations.
In this article, let’s see how the transactions can be used when the data access layer is built using .NET EF Core.
Save Changes and Transactions
As we know, EF core context tracks the entities. All the operations performed on the entities are performed on in-memory objects. After performing the operations when SaveChanges or SaveChangesAsync is called, all those changes are stored in the underlying database. This operation is by default atomic.
This means if any of the operations performed on in memory objects fail during execute of SaveChanges or SaveChangesAsync, all the changes are rolled back. Hence, the database is guaranteed to be updated by all operations or database would remain unmodified due to failure of one or more operations.
Many real world applications can rely on this default behavior. Hence there is no need of explicitly initiating transactions for such applications.
If due to any reasons, default behavior is not sufficient, then transactions can be started explicitly. Let’s say an operation requires multiple calls to SaveChanges or SaveChangesAsync method. In such case, the DbContext.Database instance can be used to begin and end the transactions as shown in below code snippet.
Note that this type of transaction would work only if the multiple save changes operations were done on a single DbContext. If the save changes are performed on multiple DbContext instances, then this type of transactions would not work. Cross-Context transactions are not converted in this article. Maybe, I will try to write about them in coming articles.
If SaveChanges fail due to any reason, the transaction can be rolled back until last successful
Savepoint. After coming to last
savepoint, the transaction is in the same state as if the remaining part of transactions has not been executed yet.
It is also possible to create and name the
savepoints manually. In this way, a transaction can be rolled back to one of the save points. Below code example shows how the transaction can be rolled back to a specific save point.
Some databases may not provide support for transactions. In such cases, such databases may throw exceptions in case transactions APIs are called.
Also, the save point feature may not be supported by many databases. In such cases, an error in save changes may leave transactions in an unknown state. Hence, it should be analyzed carefully to ensure that the features are supported by underlying database in order to avoid any issues.
I hope you find this information useful. Let me know your thoughts.