Optimistic Concurrency Updates using Entity Framework in N-Tier and N-Layer Applications (Part 2)


This is my second post about “Updating data using Entity Framework in N-Tier and N-Layer Applications”. If you wanna read the basics, go to my first post here:


http://blogs.msdn.com/cesardelatorre/archive/2008/09/04/updating-data-using-entity-framework-in-n-tier-and-n-layer-applications-short-lived-ef-contexts.aspx


So!, I finished my first post saying that the code I showed does not cover scenarios where we want to manage “Optimistic Concurrency Updates & Exceptions”, I mean, let’s say any other guy changed some data, regarding that same data customer, during the timeframe after I performed my Customer query but before I actually updated it to the database. I mean, someone changed data in the database just before I called to context.AttachUpdated(customer) and context.SaveChanges(). Ok, then, the initial original user won’t be aware that he might be over writing that new data that the other second user updated… In many cases this can be dangerous depending on the application’s type.


The goal is: “How to Update data in N-Tier and N-Layer applications using Entity Framework with detached entities and managing Optimistic Concurrency Updates and Exceptions”.


I think this is a great subject and the way you gotta do it is not very clear in official documentation neither in most of the Entity Framework books and articles (at least, at this time..).


Cool! 🙂 let’s see how we can achieve this new scenario!.


Fisrt of all, the Entity Framework can enforce optimistic concurrency when generating the update and delete SQL sentence. It does this by including original values in the WHERE clause for any entity property with the ConcurrencyMode attribute value set to Fixed. But by default, if you just create an EF model, entity fields are not set as concurrency fields.


Ok, so first thing would be to change that attribute to any entity field!. and which field do we use?, well, you can use whichever you like (Name, IDs, etc.) and you even can set several fields to have ConcurrencyMode attribute value set to Fixed.


Here you can see how to change the EDM in VS.2008:





image

If you change this “Customer” entity editing the EDM, you could see how the SQL sentence update is modified adding that column in the WHERE clause for any Update or Delete Commands:






image

exec sp_executesql N’update [SalesLT].[Customer]


set [FirstName] = @0


where (([CustomerID] = @1) and ([FirstName] = @2))


‘,N’@0 nvarchar(17),@1 int,@2 nvarchar(14)’,@0=N’Cesar App Changed’,@1=29485,@2=N’Cesar Original Value’


In this case I’m using the “FirstName” field (even several fields), but, what I usually use is a TimeStamp field added to each table and entity. In that way I always can check in the same way (same field) if the record has changed.


Then, an OptimisticConcurrencyException is raised if the original row is not found.


Here you can see that exception being raised when I modified the customer’s name in the database just before updating from the application using Entity Framework:






image “Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.”

Cool!, this is great, so when we get this exception we can decide if we want the user to know it, refresh de form and maybe start again, or we could show the differences, or we could even just catch the exception, log the problem  in a trace and just save the last operation. Ok!, but think!, what is wrong when we are using detached entities?. Sure! you got it!, the problem is that in order for the EF context to to know the original values, you’d need to use the same context since the very moment we started quering the database, updating, and so on. But, remember we are in a scenario (N-Tier and N-Layered apps) where EF contexts have a short life because we use stateless server objects and we detach the EF entity objects, and when we attach again the updated entity, we do it on a completely new context!, that new context knows nothing about the original query and the original values!! (if you wanna see more about this, just read my first post).


For instance, if we just set the concurrency mode to FIXED to any field and we use the code I showed in my first post about EF and data updates in N-Tier apps, it won’t work!. Why?, because when the business class is getting the updated data entity back, the context does not know the real original values, and if we use the first method I explained in my first post (using the AttachUpdated() custom extensor method and the context.TryGetObjectByKey()), the WHERE clause is going to compare it with current database values (which are not the real original values). Or if we use the second method I showed (using just the SetAllModified()), it is even worse, in this case it does not even know anything about the current database values and it is going to place in the WHERE clause exactly the same value we want to update, so in this second case, it will always throw an exception!!!.


So, what do we need in order to make this to work?, of course, we’d need to “artificially” re-construct the context like if we had the same context!, I mean, we need to keep somewhere the original values we got the first time we queried the database, and attach it to the second short lived context, right before we attach the updated entity. In this case, it works like a charm!. 🙂


Let’s see the code. Regarding the query business methods and WCF Service when we are just querying, we don’t need to change anything, we return data to the client tier in the same manner. But then, when we get the data in the client tier (let’s say in a WPF, Silverlight or WinForms app) we need to keep that data in a safe place. I mean, we need to clone the entity data so after the user has changed the data fields in the form and presses the Save button, we’ll actually send the updated data entity plus the original values, so the server components will be able to re-create a context who will know how to handle optimistic concurrency.


But, before getting deep into the server “update methods”, I wanna notice another problem. Entity Framework actually lacks of a “Clone()” entity method. First option (a really bad one) would be querying twice to the server WCF services so we get two data entities. But this approach is ugly because we loose performance and also there is a small possibility that while we query again, the data in the database has chaged. So, no way!, we need to clone our detached entity in the client side!


OK, for that, I’m using my own custom method (as a utility method in the client layers) which is based on memory streams copies:






//(CDLTLL)
//Cloner by memory – Short code
public static T CloneSerializing<T>(this T entityObject) where T : EntityObject, new()
{
    DataContractSerializer datContractSer = new DataContractSerializer(entityObject.GetType());
    MemoryStream memoryStream = new MemoryStream();
    datContractSer.WriteObject(memoryStream, entityObject);
    memoryStream.Position = 0;
    T clonedObject = (T)datContractSer.ReadObject(memoryStream);
    return clonedObject;                       
}


You could find faster ways to clone EF detached entites, but the thing I like about this is that it is based just on a few lines of code.


For instance, Matthieu MEZIL has made an entity  cloner based on Reflection:
http://msmvps.com/blogs/matthieu/archive/2008/05/31/entity-cloner.aspx


Take a look. It could be faster (I have not compared it), but it is a lot of code…, well, you can choose. 🙂


Then, we get to the fun stuff, let’s see how we handle the server code regarding updates and optimistic concurrency.


First of all, we need a slightly different WCF Service method, because we need to get two entities (original values and updated values):






public void CustomerBll_UpdateCustomerEx(Customer newCustomer, Customer originalCustomer)
{
    new CustomerBll().UpdateCustomerOptimisticConcurrency(newCustomer, originalCustomer);
}


And then, our business class method, the “core” of this post!! 🙂






public void UpdateCustomerOptimisticConcurrency(Customer detachedCustomer, Customer originalCustomer)
{
    try
    {               
        //(CDLTLL) Original entity must first be reunited with a Context
        context.Attach(originalCustomer);


        //(CDLTLL)Apply property changes to all referenced entities in context               
        context.ApplyReferencePropertyChanges(detachedCustomer, originalCustomer);


        // Apply entity properties changes to the context
        context.ApplyPropertyChanges(detachedCustomer.EntityKey.EntitySetName, //”Customers”,
                                                           detachedCustomer);
        //(CDLTLL) EF SaveChanges() persists all updates to the store and
        // resets change tracking in the object context.               
        context.SaveChanges();
    }
    catch (OptimisticConcurrencyException e)
    {
        //(CDLTLL) Concurrency Exception Management
        //context.Refresh(RefreshMode.ClientWins, newCustomer); // Last in wins
        //myLogger.Write(e);
        //context.SaveChanges();
        //Manage or throw the exception


        throw (e);


    }
}


Here we first tell to the context what were the original values in the database when we first queried, then it is when we tell to the context about our updated data and we actually do it not just regarding our entity, we also apply changes to all referenced entities within our EF graph (using our custom extensor method called context.ApplyReferencePropertyChanges) so when we finally call to “context.SaveChanges()“, the EF will smoothly handle the “Optimistic Concurrency Updates and/or its Exception”!, it will actually look for original values set in the WHERE clause of our Update/Delete final sentence. Cool!. 🙂


FINAL THOUGHTS


It would be nice to have a way to actually hide the original values (hidden cloned entity) within the regular detached entity object. Doing so, it would be transparent for the developer who is working on the client layers…


You can also take a look to ENTITYBAG (by Danny Simmons). I’m not using it at all in the code I’ve been talking about.


Perseus: Entity Framework EntityBag


http://code.msdn.microsoft.com/entitybag/


EntityBag – Wrap-up and Future Directions


http://blogs.msdn.com/dsimmons/archive/2008/01/28/entitybag-wrap-up-and-future-directions.aspx 


EntityBag Part II – Modes and Constructor


http://blogs.msdn.com/dsimmons/archive/2008/01/20/entitybag-part-ii-modes-and-constructor.aspx


EntityBag Modes with the Entity Framework (John Papa)


http://nypapa.com/all/entitybag-modes-with-the-entity-framework/ 


Coming to the Entity Framework: A Serializable EntityBag for n-Tier Deployment


http://oakleafblog.blogspot.com/2008/01/coming-to-entity-framework-serializable.html


The EntityBag concept is actually quite neat and it solves many points I talked about, but it has also several restrictions:


First of all, there’s the fact that it requires us to run .Net and the EF on the client, and it also requires that the code for your object model has to be available on the client, so, you loose the great discover and interoperability we have with basic Web Services (or advanced WCF wsHttp or basicHttp services, etc.), so I guess ENTITYBAG is not interoperable with Java or any non .NET language.

Comments (13)

  1. En esta ocasión, me voy a salir un poco de la línea regular que llevo en mi blog para hacer una pequeña

  2. First of all, we are talking about using Entity Framework and how it fits within N-Tier and N-Layer applications

  3. First of all, we are talking about using Entity Framework and how it fits within N-Tier and N-Layer applications

  4. Sonny Man says:

    Entity Framework in N-Tier and N-Layer Applications

  5. Padma Alluri says:

    Your approach solved the concurency issue for root object. i have a hierarchy of child objects attached to root object. updates and concurency are not working for the child enties. could you please suggest the solution or post the source code for the same?

  6. baskarangr says:

    It’t very nice article. I have one problem with "AttachUpdated" Method. in this method we are using "context.TryGetObjectByKey(objectDetached.EntityKey, out currentEntityInDb)", when it’s executing this statement, getting and assigned only parent object to the "currentEntityInDb", its not bringing the child object. could you please tell me how to load the reference Object as well. It will be great help!!!!.

  7. To baskarang e.a,

    The "AttachUpdated" method has indeed some limitations. You might be interested in my "AttachObjectGraph(s)" extension method published on http://www.codeproject.com/KB/architecture/attachobjectgraph.aspx.

    Kind regards

    Rudi Breedenraedt

  8. baskarangr says:

    Hi Rudi Breedenraedt,

    Thanks for the Kind Help, I have started implementing that Extension Method. We are facing few exception when we try to AttachObjectGraph(s). if you could already implemented I can share my doubts with you or do i need to put my doubt in the codeproject site.

    Thanks,

    Baskaran.D

  9. Hello Baskaran,

    For a private conversation, you can contact me by email, you’ll find my email address on http://www.arebis.be, or by choosing "Email Me" on my blog http://www.codetuning.net.

    Kind regards

    Rudi

  10. DreamWeaver says:

    It is nice article. Thanks. But it describes only how concurrency can be managed for the scalar properties. And what about navigation properties? There is no any ‘ConcurrencyMode’ property in the EDM designer for navigation porperties. And when you try to apply changes of navigation properties, you will never receive OptimisticConcurrency exception. But I need some way to do it. Do you have some ideas, how it can be implemented?

  11. Prem Prakash says:

    public void Save(object entity)

           {

               using (var transaction = Connection.BeginTransaction())

               {

                   try

                   {

                       SaveChanges();

                       transaction.Commit();

                    }

                    catch (OptimisticConcurrencyException)

                    {

                        if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Deleted || ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Modified)

                               this.Refresh(RefreshMode.StoreWins, entity);

                         else if (ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Added)

                               Detach(entity);

                         AcceptAllChanges();

                         transaction.Commit();

                     }

               }

           }

  12. Samulski says:

    Dear Cesar de la Torre and all readers,

    7 years after this article is written, I'm trying to store the byte array into the viewstate and on postback trying to Deserialize the object to it's original value. But I get the error below. When I deserialize after serializing, everything works when it's in the same context. I get the following error:

    An exception of type 'System.Runtime.Serialization.SerializationException' occurred in System.Runtime.Serialization.dll but was not handled in user code

    Additional information: Error in line 1 position 216. Expecting element 'ResourceServer' from namespace 'schemas.datacontract.org/…/OAuthServerBroker.EF&.. Encountered 'Element'  with name 'ResourceServer_5DA1189B7A0794008AAE3D95D68DD206BB4C4ED64A767C489B9FC5533F75B9F6', namespace 'schemas.datacontract.org/…/System.Data.Entity.DynamicProxies&.

    Here is my Deserialization code:

           public static T DataContractDeserialize<T>(byte[] byteArray, Type type)

           {

               DataContractSerializer dataContractSerializer = new DataContractSerializer(type);

               MemoryStream ms = new MemoryStream();

               ms.Write(byteArray, 0, byteArray.Length);

               ms.Position = 0;

               T OldObject = (T)dataContractSerializer.ReadObject(ms);

               return OldObject;

    }