Telerik blogs

What usually is of great importance to developers that use ORM tools, are issues related to concurrent updates or deletes that can potentially cause a valuable loss of data, and how the ORM framework handles the cases.

Because of the state-less nature of HTTP the object-scope is re-initialized on every postback. That is an operation on its own, future blog-posts will cover some of the best practices approaches to object scope life-cycle management.

The problem with stateless scenarios is that all change-tracking that the object-scope does for us in a desktop scenario is lost on every postback so there is a possibility that between postbacks someone has edited or deleted the data with which we are operating.

I have seen more than one possible solution implemented by Telerik OpenAccess ORM users like keeping the scope in the session state or using a static application-wide object scope. They are all somewhat incomplete because the object scope is not a serializable so it would not work for a big web project that stores the session in a SQL Server database. The static scope is also not a solution because it can lead to nested transactions.

What i will share in this post is a verified and proven approach that Telerik OpenAccess ORM provides and guaranties no data-loss. It is the IDataObjectKey API. It is an interface containing only one get property – DataObjectKey. The DataObjectKey is a string representing the identity and version of an OpenAccess persistent object. That means that the DataObjectKey string is specific for a persistent object before it gets modified. I guess some of the users might have already found the con with the usage of DataObjectKey. That is the obligation of using version as concurrency control mechanism. Usually not a show-stopper but it is worth mentioning.

So how do we use this DataObjectKey to control concurrency in a web app? Here is a sample implementation that should provide answers to many:

There is a Person class to be presented in a RadGrid. The grid is bound with a simple Linq query returning all the Person persistent objects from the database.

    protected void RadGrid1_NeedDataSource(object source, Telerik.Web.UI.GridNeedDataSourceEventArgs e)
   
{
       
var result = from per in scope.Extent<Person>()
                    
select per;
       
RadGrid1.DataSource = result;
   
}

And here is our Person class implementing IDataObjectKey:

    [Telerik.OpenAccess.Persistent(IdentityField = "id")]
   
public class Person:IDataObjectKey
   
{
       
private int id;
       
[FieldAlias("id")]
       
public int Id
       
{
           
get { return id; }
           
set { id = value; }
       
}
       
//Some more fields and properties here..
        public string DataObjectKey
       
{
           
get{return Telerik.OpenAccess.DataObjectKey.Obtain(this);}
       
}
    
}

As simple as it can be. Now lets assume that we have the option to edit the items in the RadGrid. The problematic scenario would be if one clicks the Edit button, types some new values and then commits the update by pressing the Update button, but someone has updated the record in between. What we do to be sure that we do not overwrite the changes of somebody is obtain the DataObjectKey of the object to be edited when the edit button is pressed and store it in the Session state.

    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;  
   
}

Then, after the Update button is clicked we take the value from the Session and pass it as a parameter to the static DataObjectKey.Check(string key, IObjectContext context) method. The second parameter is the object scope we use for our operation. The Check method internally checks if the instance still corresponds to the same DataObjectKey e.g. the instance has not been changed since the last postback and returns the instance in case that is true. Otherwise OptimisticVerificationException is thrown. From this point on it is fairly easy to implement whatever logic we decide to handle the case – like catching the exception and providing the user with an appropriate message etc..

    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;
       
}
   
}

Comments

Comments are disabled in preview mode.