Handling Transactions
A transaction is an indivisible unit of work and it is used to ensure data integrity. Transactions control the concurrent access of data by multiple programs. In the event of a system failure, transactions ensure that after recovery the data is in a consistent state. All operations on a persistent class need to be carried out within a transaction. Transactions are started automatically and all changes are persisted in-memory. A transaction can end in two ways: with a commit or a rollback. There are several methods exposed by the OpenAccessContext that could be useful for handling transactions:
- SaveChanges - attempts to save all the changes to the database, however, if any statement within the transaction fails, the transaction is rolled back, undoing all the effects of the statements in the transaction. Read more
- ClearChanges - rollbacks all changes in the context. Read more
- HasChanges - indicates whether the context contains any changes. Read more
- GetChanges - returns all pending inserts, deletes or updates. Read more
- FlushChanges - flushes all current changes to the database but keeps the transaction running. Read more
- Refresh - refreshes an objects according to the specified mode. A RefreshMode value that indicates whether property changes in the context are overwritten with values from the data source. Read more
Committing Transactions
In order to write changes to the database you need to call the SaveChanges method of the context.
dbContext.SaveChanges();
dbContext.SaveChanges()
Rollbacking Changes
To rollback all changes in the context, the ClearChanges method of the context should be used.
dbContext.ClearChanges();
dbContext.ClearChanges()
Checking for Changes in the Context
The HasChanges property allows you to check for any changes in the context.
using (FluentModel dbContext = new FluentModel())
{
Product product = new Product();
bool hasChanges = dbContext.HasChanges; // False
dbContext.Add(product);
hasChanges = dbContext.HasChanges; // True
dbContext.SaveChanges();
}
Using dbContext As New FluentModel()
Dim _product As New Product()
Dim hasChanges As Boolean = dbContext.HasChanges ' False
dbContext.Add(_product)
hasChanges = dbContext.HasChanges ' True
dbContext.SaveChanges()
End Using
Getting Changes that Will be Performed During the Next Commit
The GetChanges method allows you to get all pending inserts, deletes or updates.
using (FluentModel dbContext = new FluentModel())
{
// Update a category
Category category = dbContext.Categories.First();
category.CategoryName = "New Name";
// Create two new categories
Category newCategory1 = new Category();
newCategory1.CategoryName = "New Category";
Category newCategory2 = new Category();
newCategory2.CategoryName = "New Category2";
dbContext.Add(new Category[] { newCategory1, newCategory2 });
dbContext.Delete(new Category[] { newCategory1, newCategory2 });
// GetChanges
Telerik.OpenAccess.ContextChanges contextChanges = dbContext.GetChanges();
IList<Category> inserts = contextChanges.GetInserts<Category>();
IList<Category> updates = contextChanges.GetUpdates<Category>();
IList<Category> deletes = contextChanges.GetDeletes<Category>();
Console.WriteLine("{0} objects will be inserted", inserts.Count);
Console.WriteLine("{0} objects will be updated", updates.Count);
Console.WriteLine("{0} objects will be delete", deletes.Count);
}
Using dbContext As New FluentModel()
' Update a category
Dim category_Renamed As Category = dbContext.Categories.First()
category_Renamed.CategoryName = "New Name"
' Create two new categories
Dim newCategory1 As New Category()
newCategory1.CategoryName = "New Category"
Dim newCategory2 As New Category()
newCategory2.CategoryName = "New Category2"
dbContext.Add(New Category() {newCategory1, newCategory2})
dbContext.Delete(New Category() {newCategory1, newCategory2})
' GetChanges
Dim contextChanges As Telerik.OpenAccess.ContextChanges = dbContext.GetChanges()
Dim inserts As IList(Of Category) = contextChanges.GetInserts(Of Category)()
Dim updates As IList(Of Category) = contextChanges.GetUpdates(Of Category)()
Dim deletes As IList(Of Category) = contextChanges.GetDeletes(Of Category)()
Console.WriteLine("{0} objects will be inserted", inserts.Count)
Console.WriteLine("{0} objects will be updated", updates.Count)
Console.WriteLine("{0} objects will be delete", deletes.Count)
End Using
Flushing Changes
The FlushChanges method allows you to flush all current changes to the database but keeps the transaction running. In other words, the FlushChanges method temporarily saves changes made to persistent objects to the data store. To get a better idea of the FlushChanges method, take a look at the following example. First, a customer is retrieved from the database and then it is changed. At this point, the customer object is marked as dirty and if you get the list of dirty objects managed by the context, this object will be included in the collection. When you call FlushChanges, Telerik Data Access temporarily saves the changes made to the customer object to the server. The "is dirty" flag for the customer object is reset to False. Respectively, if you get the list of dirty objects managed by the context after FlushChanges is called, the customer object will not be included in the list.
using (FluentModel dbContext = new FluentModel())
{
Customer customer = dbContext.Customers.FirstOrDefault();
customer.ContactName = customer.ContactName + "Changed";
IList<Customer> dirtyCustomers = dbContext.GetChanges().GetUpdates<Customer>();
Debug.Assert(dirtyCustomers.Count == 1);
dbContext.FlushChanges();
dirtyCustomers = dbContext.GetChanges().GetUpdates<Customer>();
Debug.Assert(dirtyCustomers.Count == 0);
}
Using dbContext As New FluentModel()
Dim _customer As Customer = dbContext.Customers.FirstOrDefault()
_customer.ContactName = _customer.ContactName & "Changed"
Dim dirtyCustomers As IList(Of Customer) = dbContext.GetChanges().GetUpdates(Of Customer)()
Debug.Assert(dirtyCustomers.Count = 1)
dbContext.FlushChanges()
dirtyCustomers = dbContext.GetChanges().GetUpdates(Of Customer)()
Debug.Assert(dirtyCustomers.Count = 0)
End Using
Refreshing Objects
The Refresh method updates a collection of objects or a single object in the context with data from the database. After Refresh is called, the object’s original values will always be updated with the data source value, but the current values might or might not be updated with the data source value. This depends on the RefreshMode value. The OverwriteChangesFromStore value means that the passed object should be updated to match the data source values. The PreserveChanges value means that the actual changes in the object will be kept but all clean data will be re-read.
using (FluentModel dbContext = new FluentModel())
{
Car car = dbContext.Cars.FirstOrDefault();
car.Make = car.Make + "_";
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, car);
}
Using dbContext As New FluentModel()
Dim _car As Car = dbContext.Cars.FirstOrDefault()
_car.Make = _car.Make & "_"
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, _car)
End Using