Managing Concurrency
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
By default, Telerik Data Access implements an optimistic concurrency control. This means that locks are not held on data in the data store between the data is queried and the data is updated. Telerik Data Access checks for changes in the database before saving changes to the database. Any conflicts will cause an Telerik.OpenAccess.Exceptions.OptimisticVerificationException. The way you handle concurrency exceptions depends on the business rules required by your application. When making updates in such high concurrency scenarios, you could use the Refresh method frequently. 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, it controls how changes are propagated. 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. When an OptimisticVerificationException occurs, you could handle it by calling Refresh, and specifying whether the conflict should be resolved by preserving data in the object or by updating the object with the data from the data store, as in the following example.
In this example, changes to the Product object cause an OptimisticVerificationException. The concurrency conflict is resolved by refreshing the object, making the changes again and re-saving it.
using (EntitiesModel dbContext = new EntitiesModel())
{
Product product = dbContext.Products.FirstOrDefault();
try
{
product.Name = product.Name + "-";
dbContext.SaveChanges();
}
catch (Telerik.OpenAccess.Exceptions.OptimisticVerificationException ex)
{
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, product);
product.Name = product.Name + "-";
dbContext.SaveChanges();
}
}
Using dbContext As New EntitiesModel()
Dim _product As Product = dbContext.Products.FirstOrDefault()
Try
_product.Name = _product.Name & "-"
dbContext.SaveChanges()
Catch ex As Telerik.OpenAccess.Exceptions.OptimisticVerificationException
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, _product)
_product.Name = _product.Name & "-"
dbContext.SaveChanges()
End Try
End Using
The OpenAccessContext exposes a method named GetLastConflicts, which allows you to get the ObjectKeys for all failing object.
using (EntitiesModel dbContext = new EntitiesModel())
{
Product product = dbContext.Products.FirstOrDefault();
try
{
product.Name = product.Name + "-";
dbContext.SaveChanges();
}
catch (Telerik.OpenAccess.Exceptions.OptimisticVerificationException ex)
{
IList<ConcurrencyConflict> conflicts = dbContext.GetLastConflicts();
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, product);
product.Name = product.Name + "-";
dbContext.SaveChanges();
}
}
Using dbContext As New EntitiesModel()
Dim _product As Product = dbContext.Products.FirstOrDefault()
Try
_product.Name = _product.Name & "-"
dbContext.SaveChanges()
Catch ex As Telerik.OpenAccess.Exceptions.OptimisticVerificationException
Dim conflicts As IList(Of ConcurrencyConflict) = dbContext.GetLastConflicts()
dbContext.Refresh(Telerik.OpenAccess.RefreshMode.OverwriteChangesFromStore, _product)
_product.Name = _product.Name & "-"
dbContext.SaveChanges()
End Try
End Using
By default the GetLastConflicts method will return only the first conflict because calculating the rest takes too much time. However, if you want to get all conflicts, you can pass the ConcurrencyConflictsProcessingMode.AggregateAll enumeration value in the SaveChanges method. The AggregateAll option executes all insert, update and delete statements regardless of an error. If all failures during commit should be collected and not only the first one, then the AggregateAll mode should be used. This can be a time consuming operation. The other possible value is StopOnFirst, which will stop the processing after the first error.
Collecting all failures during commit can be a time consuming operation.
dbContext.SaveChanges(ConcurrencyConflictsProcessingMode.AggregateAll);
dbContext.SaveChanges(ConcurrencyConflictsProcessingMode.AggregateAll)