Telerik OpenAccess Classic

Telerik OpenAccess ORM Send comments on this topic.
Best Practices for Handling Concurrency in Web Scenarios
Programmer's Guide > OpenAccess ORM Classic (Old API) > OpenAccess Tasks > Best Practices for Handling Concurrency in Web Scenarios

Glossary Item Box

This documentation article is a legacy resource describing the functionality of the deprecated OpenAccess Classic only. The contemporary documentation of Telerik OpenAccess ORM is available here.

There are two approaches of handling the process in the appropriate manner - the first involves using OpenAccessDataSource component for managing CRUD operations. All the logic for detecting and exposing a concurrent update or delete is built in the OpenAccessDataSurce and it is very straight-forward to track concurrent issues there.

When detecting such problem during an update or delete operation OpenAccessDataSource throws an OptimisticVerificationException which can be handled in the event handlers for the updating and deleting events.

In more advanced web projects though using a data source is not always an option. Queries are executed in code-behind and all CRUD operations are handled in that manner.

The answer for all concurrency concerns in those situations comes with the implementation of the IDataObjectKey interface to the persistent classes that are to be modified or deleted.

The IDataObjectKey property is a unique string for every persistent class that contains information about the identity and version of the object. Because the version of an object is updated on modification this key can be used to give us information on weather a certain object has been modified.


Sample implementation:

There are entities of the Person type to be presented in a RadGrid. The grid is bound with a simple Linq query returning all the Person objects from the database.

Our Person class implements the IDataObjectKey interface:

C# Copy Code
[Telerik.OpenAccess.Persistent(IdentityField = "id")]
public class Person:IDataObjectKey
{
  
private int id;
  [FieldAlias(
"id")]
  
public int Id
  {
       get {
return id; }
       set { id = value; }
  }
  ...

 
public string DataObjectKey
 {
     get{
return Telerik.OpenAccess.DataObjectKey.Obtain(this);}
 }
}

 

VB .NET Copy Code
Public Class Person
 Implements IDataObjectKey
   Private id_Renamed As Integer
   <FieldAlias("id")> _
   Public Property Id() As Integer
  Get
   Return id_Renamed
  End Get
  Set(ByVal value As Integer)
   id_Renamed = value
  End Set
   End Property
...
  Public ReadOnly Property DataObjectKey() As String
   Get
    Return Telerik.OpenAccess.DataObjectKey.Obtain(Me)
   End Get
  End Property
End Class

The RadGrid has the edit command enabled. Our solution for the concurrent updates is the following:
•    When the Edit button is pressed we obtain the person object to be updated and store its DataObjectKey property into the Session state. We do that operation in the EditCommand event handler of the RadGrid:

C# Copy Code
protected void RadGrid1_EditCommand(object source, Telerik.Web.UI.GridCommandEventArgs e)
{
   IDictionary values =
new Hashtable();
   ((GridDataItem)e.Item).ExtractValues(values);
   
string id = (string)values["Id"];
   Person updatingPerson = (Person)scope.GetObjectById(Database.OID.ParseObjectId(
typeof(Person), id));
   
string keyAndVersion = updatingPerson.DataObjectKey;
   Session[
"updatingPerson" + id.ToString()] = keyAndVersion;   
}
VB .NET Copy Code
Protected Sub RadGrid1_EditCommand(ByVal source As Object, ByVal e As Telerik.Web.UI.GridCommandEventArgs)
 Dim values As IDictionary = New Hashtable()
 CType(e.Item, GridDataItem).ExtractValues(values)
 Dim id As String = CStr(values("Id"))
 Dim updatingPerson As Person = CType(scope.GetObjectById(Database.OID.ParseObjectId(GetType(Person), id)), Person)
 Dim keyAndVersion As String = updatingPerson.DataObjectKey
 Session("updatingPerson" & id.ToString()) = keyAndVersion
End Sub

•    After typing the new values into the edit form of the grid the Update button is pressed. We obtain the record once again in the code-behind but this time using the static DataObjectKey.Check(string dataObjectKey, IObjectCotnext context) method. In this method OpenAccess checks if the instance represented by the DataObjectKey has been modified since the DataObjectKey was obtained. If the instance contains a new DataObjectKey e.g. it has a new version, an OptimisticVerificationException is thrown, else the object is returned and we can commit an update on it.

C# Copy Code
protected void RadGrid1_UpdateCommand(object source, GridCommandEventArgs e)
   {
       IDictionary values =
new Hashtable();
       ((GridEditableItem)e.Item).OwnerTableView.ExtractValuesFromItem(values,((GridEditableItem)e.Item));

       
string id = (string)values["Id"];
       
string keyAndVersionOld = (string)Session["updatingPerson" + id.ToString()];
       Person updatingPerson = (Person)DataObjectKey.Check(keyAndVersionOld, scope);
       
if (updatingPerson != null)
       {
           scope.Transaction.Begin();
           updatingPerson.Id =
int.Parse(values["Id"].ToString());
           updatingPerson.FirstName = values[
"FirstName"].ToString();
           updatingPerson.LastName = values[
"LastName"].ToString();
           updatingPerson.AddressId =
int.Parse(values["AddressId"].ToString());
           scope.Transaction.Commit();
           Session[
"updatingPerson" + id.ToString()] = null;
       }
}
VB .NET Copy Code
Protected Sub RadGrid1_UpdateCommand(ByVal source As Object, ByVal e As GridCommandEventArgs)
  Dim values As IDictionary = New Hashtable()
  CType(e.Item, GridEditableItem).OwnerTableView.ExtractValuesFromItem(values,(CType(e.Item, GridEditableItem)))
  Dim id As String = CStr(values("Id"))
  Dim keyAndVersionOld As String = CStr(Session("updatingPerson" & id.ToString()))
  Dim updatingPerson As Person = CType(DataObjectKey.Check(keyAndVersionOld, scope), Person)
  If updatingPerson IsNot Nothing Then
   scope.Transaction.Begin()
   updatingPerson.Id = Integer.Parse(values("Id").ToString())
   updatingPerson.FirstName = values("FirstName").ToString()
   updatingPerson.LastName = values("LastName").ToString()
   updatingPerson.AddressId = Integer.Parse(values("AddressId").ToString())
   scope.Transaction.Commit()
   Session("updatingPerson" & id.ToString()) = Nothing
  End If
End Sub

From this point on it is fairly easy for one to implement his/her own logic for proceeding with the situation. For example the Check() method can be called in a try/catch block so if there is a  concurrency problem we can just prompt the user with appropriate message.