I just wrote an article for MSDN Magazine (due out in December) about developing n-tier applications for both LINQ to SQL and the Entity Framework. While researching the topic, I noticed a certain awkwardness with the Entity Framework API when it came to managing concurrency with a timestamp fields, as compared with LINQ to SQL. L2S has an overloaded Attach method that accepts a bool asModified parameter indicating the presence of a timestamp field used for concurrency. The Entity Framework, on the other hand, requires attaching the original unmodified entity, then calling ApplyAllChanges, passing in the modified entity. This doesn’t make a whole lot of sense when using a timestamp for concurrency checking, because the only original value you care about is the timestamp, which is passed in unchanged as part of the modified entity. Using timestamps is a great idea because it relieves the client from needing to cache the object’s original values and reduces the amount of data sent over the wire when you’re sending entities to a service for persistence. In the article, I got around this by querying the database from the service using the key value of the modified entity. Yuck!
Before you throw up your hands in despair, I have good news for you. Danny Simmons, a developer and program manager on the EF team, just came up with a couple extension methods to obviate the need for original entity values. Rather than taking the original entity and applying property changes from a detached object that has been modified, you can simply take the detached object and set the state of each property to modified. What you’re essentially saying is, “Look, I already have a modified entity, just change the state of each property from ‘Unchanged’ to ‘Modified’ so that when I call SaveChanges the updates are persisted.”
There’s one more wrinkle to this scenario: using Data Transfer Objects (DTOs) instead of EF entities in the Data Access Layer (DAL). Although v.2 of the Entity Framework will allow you to use DTOs directly as EF entities, this is not yet fully supported in v.1, meaning that an UpdateOrder method would accept a DTO.Order object, which you would use to create an L2E.Order object. To accomplish this, I created an extension method for ObjectContext called CreateEntityFromObject which accepts a DTO and uses reflection to copy properties from the DTO to the Entity and create an EntityKey. The code to persist changes to an Order entity now looks like this:
static DTO.Order UpdateOrder(DTO.Order order) { using (NorthwindEntities db = new NorthwindEntities()) { // Create new order entity from DTO.Order Order updatedOrder = db.CreateEntityFromObject <Order>("OrderSet", order); // Attach modified order (with original timestamp) db.AttachAsModified(updatedOrder); try { db.SaveChanges(); } catch (OptimisticConcurrencyException conflictEx) { Console.WriteLine(conflictEx.Message); return null; } return GetOrder(order.OrderID); } }
Here is the CreateEntityFromObject extension method:
public static TEntity CreateEntityFromObject<TEntity>
(this ObjectContext context, string entitySetName, object dto) where TEntity : IEntityWithKey, new() { // Create a new entity TEntity entity = new TEntity(); // Copy properties foreach (PropertyInfo dtoProp in dto.GetType().GetProperties()) { PropertyInfo entityProp = typeof(TEntity).GetProperty(dtoProp.Name); object propValue = dtoProp.GetValue(dto, null); entityProp.SetValue(entity, propValue, null); } // Set the entity key entity.EntityKey = context.CreateEntityKey(entitySetName, entity); // Return the entity return entity; }
To see all of this in action, you can download the code for my sample concurrency application. Enjoy!
I have just written a blog post that describes a bug in EF v.1, which has the effect of limiting the use of the AttachAsModified method such that you are required to supply the original foreign key values:
https://blog.tonysneed.com/?p=140
If you are using a timestamp for concurrency and do not wish to retain other original values, then your only alternative is to re-query the database for the original entity. đŸ˜¦
Hi Tony,
I enjoyed the article and was able to use in my own solution. However, in your extension method “CreateEntityFromObject” you’re using some reflection to assign values from the business object to the EF data object. Our database uses a different naming convention than does our application development(and using reflection would require having the properties be the same). I’d like to avoid having database naming conventions in the application code, so I took apart your extension method and tried to hack it into the method itself:
using (SQL context = new SQL(_conn))
{
// this works but uses reflection
//Work updatedWork = context.CreateEntityFromObject(“Works”, this);
Work updatedWork = new Work();
updatedWork.Id = Id; // P/K
updatedWork.UserIdAssignTo = UserIdAssignTo;
updatedWork.RowVer = Version; // for optimistic concurrency
Work.EntityKey = context.CreateEntityKey(“Works”, updatedWork);
// getting error on line above, the red under line is under Work.EntityKey
context.AttachAsModified(updatedWork);
try { context.SaveChanges(); }
catch (OptimisticConcurrencyException ex) { }
}
The line: Work.EntityKey = context.CreateEntityKey(“Works”, updatedWork); is throwing the following error:
An object reference is required for the non-static field, method, or property EntityKey.get
I’m a little confused as to how/why it works on yours, but not the code I wrote. I followed the inheritance chain from “Work”(that’s my EF object), It inherits from System.Data.Objects.DataClasses.EntityObject, and EntityObject implements the IEntityWithKey interface, with both public get/set. So it seems I should get access to it. Any help would be greatly appreciated.
Hi Chris,
Be advised this posting and article came out prior to the second version of Entity Framework that shipped with .NET 4.0 and Visual Studio 2010. If you are using POCO classes (Plain Old CLR Objects), then you don’t need to copy values from one object to another, because you are using only one set of classes.
Cheers,
Tony