IC窗口
community.icburner.com
Better N-Tier Concurrency Management for the Entity Framework(Sum)

Better N-Tier Concurrency Management for the Entity Framework

Posted in Technical at 11:03 am by Tony

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!

12.11.08

Bug in EF v.1 Limits N-Tier Scenarios

Posted in Technical at 8:02 am by Tony

About a month ago I wrote a blog post on an extension method for the Entity Framework called AttachAsModified, which was offered by Danny Simmons shortly after PDC as a way to perform disconnected updates from an n-tier service. Unfortunately, I discovered what I consider to be a bug in EF v.1 that limits your ability to use the AttachAsModified extension method. The problem surfaces when the entity you’re trying to update has reference properties to related entities. For example, say you have an Order entity with a Customer navigation property that relates it to the Customer entity. If Order contained a CustomerID property representing the foreign key, you could simply set its value. But the EF models this as an association between Order and Customer and does not include CustomerID as a foreign key in the Order entity. (The EF team, nevertheless, is considering allowing foreign keys into the model.)

The way to set reference properties in EF is to set the EntityKey of the reference property to a new key based on the foreign key value you want to set it to. For example, if you want to change the Customer that an Order is associated with, you need to do the following:

updatedOrder.CustomerReference.EntityKey =

new EntityKey("NorthwindEntities.CustomerSet",

"CustomerID", newCustomerID);

The problem is that, when you call SaveChanges on the ObjectContext, EF includes the non-reference Customer property in the WHERE clause of the UPDATE statement.

exec sp_executesql

N'update [dbo].[Orders]

set [CustomerID] = @0,

[OrderDate] = @1,

-- other fields set to null

where (([CustomerID] is null and ([OrderID] = @2))

and ([RowVersion] = @3))

N'@0 nchar(5),@1 datetime,@2 int,@3 binary(8)',

@0=N'ANATR',@1='1996-09-29 00:00:00:000',@2=10308,@3=0x000000000004BF3D

If you leave it null, or set it to anything other than the original value, this will result in an OptimisticConcurrencyException. This means you are required to pass in the Order’s original CustomerID value, which doesn’t make sense if you are using a timestamp column to manage concurrency (which is the purpose of the [RowVersion] in the above WHERE clause.

For this reason, I can’t bring myself to require the client app to retain original foreign key values, and I’m left having to re-query the database for the original entity, then set the timestamp property to that passed in with the updated entity. After detaching the entity to commit this change and mark it as ‘unmodified’, you can then modify the original entity properties to the values of the updated entity and call SaveChanges. While re-querying the database for the original entity isn’t pretty (and quite inefficient), I think it’s a better alternative to passing in the original values from the client (an approach advocated by MS architect Cesar de la Torre), because it won’t require code changes in the client when the EF team fixes the bug (hopefully before the release of v. 2!).

Here is a sample app that demonstrates the bug and also how to avoid it by re-querying the database. Cheers.

Updating data using Entity Framework in N-Tier and N-Layer Applications (short lived EF contexts) - (Part 1)

First of all, we are talking about using Entity Framework and how it fits within N-Tier and N-Layer applications, ok?, that’s our initial scenario.

An N-Tier application is an application where you have 3 or more physical tiers. I mean with that things like, “Presentation/Client Tier”, “Application/Business Server Tier” and “Data Tier” (a database server in most of the cases) and nowadays we use web services (or even better, WCF Services) to communicate between presentation tier and the application server tier. Here you see a simple & typical picture about N-Tier architecture:

clip_image002

As you can see, all the client applications (we could be for instance WinForms, WPF, or even Silverlight apps) need to remotely access to the application Server tier using for example WCF Services.

Also, in my opinion. RIA (Rich Internet Applications) is a special subset of N-Tier apps.

A different matter is that we’d probably design our application as an N-Layer application, I mean, with several logic layers where we implement different logic tasks. For example we could have the DAL layer (Data Access Layer), BLL Layer (Business Logic Layer), BFLL Layer (Busines Façade Logic Layer), WCF Service Layer and several Presentation layers depending of the pattern we use, like MVC (Model-View-Controller), MVP (Model-View-Presenter), etc.. Also, within the N-Layer architecture, you can guess that Entity Framework fits as the DAL Layer (Data Access Classes) as well as using EF entities as our disconnected entities to pass thru all the layers, all right?

image

BTW, not all N-Layer apps should be N-Tier apps, but all N-Tier Apps must internally be designed as N-Layer. I mean, there are many cases where the less you physically split your model, the better for performance (more tiers is good for scalability but not for pure performance, due to latencies). Remember, N-Layer is about logic layers.

OK!, so if we get back to the N-Tier architecture (physical tiers), like I said, we need remote mechanisms to communicate the client tier with the application server tier (for instance, WCF Services) and therefore when we query the database from the app server tier, to obtain data (like an Order), we keep it as an EF entity, then we disconnect it from the EF context (detach), WCF serializes it and sends that disconnected entity to the presentation tier (client apps & machines).

So, most enterprise applications use an architectural pattern that needs a stateless facade for its business logic (like N-Tier & N-Layer apps). They have scenarios like WebServices or WCF, ASP.NET applications, Traditional Desktop Application (Windows Forms, WPF) consuming WCF Services, etc. in those cases data access works “disconnected oriented”, I mean, database data (in the real tables) are not blocked while the user is working in the client tier (btw, this is better for scalability). So, in the client/presentation tier, the user will be working and maybe, he changes that entity’s data (which is disconnected from the server) and after a while (at any time) the user submits that data to the application server and then the DAL layer (Entity Framework and LINQ to EF, in our case) will update that data into the database. Right?. Well, if we just do that, it will be just a “Last-In-Wins” or what I call, a “Too Optimistic Update” ;-) . Well, in many apps which are not very exigent or complex regarding updates, this can be enough, but then, we could have other apps where the users need to know if data (regarding the same entity and records he is using at client tier) has changed in the database while he was working and before he tried to update the data. In that case we should use “Optimistic Concurrency Updates” managing “Optimistic Concurrency Exceptions”.

So, I am using all the following stuff:

- Entity Framework and LINQ to Entities as our DAL Layer

- WCF as our remote communication technology

- Any .NET UI technology as our client App (WPF, WinForms, OBA or even Silverlight)

Simple way: Updating data in N-Tier applications and using Entity Framework with detached entities

In order to understand what I mean, first of all, I am going to explain how to implement just simple updates in N-Tier applications and using Entity Framework. So this case is the one I called “Last-In-Wins” or a “Too Optimistic Update” ;-). So in this first case I won’t manage any Optimistic Concurrency Exception.

First of all, we’d have a business class (BLL class) which would be using EF & LINQ to Entities so that class would be querying the database; let’s say to get a Customer data and to return that entity’s data to the client/presentation tier (thru a WCF Service). The BLL class code could be something like the following code:

public class CustomerBll

{

    private MyDataBaseEntities context;

    public CustomerBll()

    {

        context = new MyDataBaseEntities();

    }

    public Customer GetCustomer(string customerID)

    {

        var q = from c in context.Customers //.Include("Orders")

        where c.CustomerID == numCustomerID

        select c;

        var customer = q.First();

        context.Detach(customer);

        return customer;

}

Ok, if you know LINQ to entities, this is a very simple method, right?. The important thing to take into account is the following: “we are returning a disconnected/detached entity data object” passing it to the upper layers (Service layeràPresentation Layers). It means that regarding Entity Framework we’ll be working with ObjectContext objects in a “short lived context” way. Basically, when we’ll close the loop, we’ll need to re-attach modified EntityObjects, that were updated outside of an ObjectContext (in detached state). This is a very common scenario when we're using stateless facades (most N-Tier & N-Layer apps).

Coming back to our code, then we could have a WCF Service serializing & returning the disconnected/detached entity.

Here you could see the Service contract for the method:

//WCF Contract

[OperationContract]

Customer CustomerBll_GetCustomer(string customerID);

...

...

//And now, the method’s implementation in the WCF class library:

public Customer CustomerBll_GetCustomer(string customerID)

{

    return new CustomerBll().GetCustomer(customerID);

}

Ok, after that, the client app would be consuming the WCF Service and will display the entity’s data (customer’s data) let’s say in a WPF form. Remember that right now, the client app is working with a disconnected/detached entity object and there is not an ObjectStateManager tracking changes. We’ll see how it affects later on…

So, once the data is visible within a form, the user can start changing some data in that form until he decides to actually update that data into the application (for me, it means he wants to update that data into the database), pressing, for instance a big button which says “Update”. J
Right, in this very moment the client application would be calling another WCF Service method called something like UpdateCustomer(), like you see down below:

public void CustomerBll_UpdateCustomer(Customer customer)

{

    new CustomerBll().UpdateCustomer(customer);

}

Then, that WCF method is calling the real business logic method, and this is where I am going to show you how to use Entity Framework to update the detached/disconnected but modified entity which is coming back from the client tier. This is the code (remember this is the simple case, I am not managing optimistic concurrency exceptions here):

public void UpdateCustomer(Customer customer)

{

    //(CDLTLL) Entity must first be reunited with a Context

    //Note I do it with my custom extensor method for disconected environments (N-Tier, etc.)

    context.AttachUpdated(customer); //Custom extensor method

    context.SaveChanges();

}

Ok, first, in order for the context to be able to update anything (the entity) into the database using SaveChanges(), it has first to have it attached to the context. So, context.SaveChanges() is an EF regular method we always gotta use to update data (changes). But if we’d just use the method context.Attach(customer) before that, it won’t work because that customer entity is new for the context (it was originally detached, serialized and sent to the client tier) and now the context does not know nothing about it, the context “thinks” it is a new entity object and it would not know any changes to update using the context.SaveChanges().

The following is wrong code:

public void UpdateCustomer(Customer customer) //WRONG CODE

{

    //(CDLTLL) Entity must first be reunited with a Context

    context.Attach (customer); //Custom extensor method

    context.SaveChanges();//In this case, nothing happens here…

}

So in this simpler case, when you’d call SaveChanges() nothing would happen.

That is why I created a custom EF extensor method called “context.AttachUpdated()”. To be able to attach updated entities which are disconnected and coming from the client Tier.

It is the following code:

public static void AttachUpdated(this ObjectContext context, EntityObject objectDetached)

{

    if (objectDetached.EntityState == EntityState.Detached)

    {

        object currentEntityInDb = null;

        if (context.TryGetObjectByKey(objectDetached.EntityKey, out currentEntityInDb))

        {

            context.ApplyPropertyChanges(objectDetached.EntityKey.EntitySetName, objectDetached);

            //(CDLTLL)Apply property changes to all referenced entities in context

            context.ApplyReferencePropertyChanges((IEntityWithRelationships)objectDetached,

                                                                                (IEntityWithRelationships)currentEntityInDb); //Custom extensor method

        }

        else

        {

             throw new ObjectNotFoundException();

        }

    }

}

So, first of all we need to know what is new in this entity, I mean, what are the changes compared with the data we actually have in the database. In order to do that we’d need to query the database and get the actual data using the “context.TryGetObjectByKey()” method and attach it as the original data for that entity within the context.

Then, we can call the “context.ApplyPropertyChanges()” providing our new updated entity. What EF does is that it compares the original data (actual data in the database) with our new/updated entity and then it updates within the context all the property changes just regarding our specific entity (Customer, in this case). After that, when we call to “context.SaveChanges()” in the main method, EF will detect what are actually those changes and it will be able to update it into the real database (SQL Server, for instance).

Notice that after calling to “context.ApplyPropertyChanges()” I am calling another method called “ApplyReferencePropertyChanges()”. This other method is actually another extensor method which applies all my entity property changes but into all the referenced entities within our EF model (Customer could be related to Company, Order, etc.). This is an important method, otherwise we’ll be taking into account just our isolated entity (Customer). Here you can see my “context.ApplyReferencePropertyChanges()” custom context extensor method:

public static void ApplyReferencePropertyChanges(this ObjectContext context,

IEntityWithRelationships newEntity,

IEntityWithRelationships oldEntity)

{

    foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())

    {

        var oldRef = relatedEnd as EntityReference;

        if (oldRef != null)

        {

            // this related end is a reference not a collection

            var newRef = newEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as EntityReference;

            oldRef.EntityKey = newRef.EntityKey;

        }

    } 

}

So with that, we’re done!, it works ok following the method I said “Last In Wins” or a “Too Optimistic Update” ;-)

Btw, another option (it works almost the same) would be not to query the database to know the actual data (kind of original data for the context) with “context.TryGetObjectByKey()” but to say to the context that all the properties within my entity have actually changed, “just because I say it”. After that, when we call “context.SaveChanges()” it will update all the entity properties just because we said that!. J

Here you can see this other entity extensor method (In this case I coded as an entity extensor method instead as a context extensor method, ok?):

public static void SetAllModified<T>(this T entity, ObjectContext context) where T : IEntityWithKey

{

    var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);

    var propertyNameList = stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);

    foreach (var propName in propertyNameList)

    {

        stateEntry.SetModifiedProperty(propName);

    }

}

In this other case (using SetAllModified()) our code within "UpdateCustomer()" would be somethin like:

public void UpdateCustomer(Customer customer)
{
context.Attach(customer);
customer.SetAllModified(context);     // custom extension method
context.SaveChanges();
}

Cool!, this seems all great, we are happy and we are living in a wonderful world. Sure?. Well, it depends, but like I said at the beginning of this post, this code we have written 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.

Well, I guess this post is getting too long, I’ll write a second post (very soon) explaining “How to: Update data in N-Tier applications and using Entity Framework with detached entities and managing Optimistic Concurrency Updates and Exceptions”.

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.

Examples


The example in this topic is based on the Adventure Works Sales Model. In this example, an updated SalesOrderDetail object is passed to the UpdateItemChanges method, together with the original object. This enables changes to be applied without querying for the object or having to persist it in memory.

private static void ApplyItemUpdates(SalesOrderDetail originalItem,
    SalesOrderDetail updatedItem)
{
    using (AdventureWorksEntities advWorksContext = 
        new AdventureWorksEntities())
    {
        try
        {
            // Attach the original item to the object context.
            advWorksContext.Attach(originalItem);
 
            // Call the ApplyPropertyChanges method to apply changes
            // from the updated item to the original version.
            advWorksContext.ApplyPropertyChanges("SalesOrderDetail",
                updatedItem);
 
            advWorksContext.SaveChanges();
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}
 

In this example, the original SalesOrderDetail object is retrieved and then changes are applied to it based on an updated SalesOrderDetail object that is passed to the UpdateItemChanges method.

private static void ApplyItemUpdates(SalesOrderDetail updatedItem)
{
    // Define an ObjectStateEntry and EntityKey for the current object.
    EntityKey key;
    object originalItem;
 
    using (AdventureWorksEntities advWorksContext =
        new AdventureWorksEntities())
    {
        try
        {
            // Create the detached object's entity key.
            key = advWorksContext.CreateEntityKey("SalesOrderDetail", updatedItem);
 
            // Get the original item based on the entity key from the context
            // or from the database.
            if (advWorksContext.TryGetObjectByKey(key, out originalItem))
            {
                // Call the ApplyPropertyChanges method to apply changes
                // from the updated item to the original version.
                advWorksContext.ApplyPropertyChanges(
                    key.EntitySetName, updatedItem);
            }
 
            advWorksContext.SaveChanges();
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

Here's one approach to updating a reference in an entity in the mid tier of a SOA app.

public static EnduranceActivity UpdateEnduranceActivity(EnduranceActivity currentActivity, EnduranceActivity originalActivity, Route newRoute)

{

   using (FitnessAppContainer appContainer = new FitnessAppContainer(ConnectionString))

   {

      if (null != newRoute)

      {

         originalActivity.Route = newRoute;

      }

      appContainer.Attach(originalActivity);

      appContainer.ApplyPropertyChanges("FitnessAppContainer.EnduranceActivitySet", currentActivity);

      appContainer.SaveChanges();

      }

      return originalActivity;

}

We are looking at adding an option to ApplyPropertyChanges that will update references as well for V2 as well as other features that will make this easier.

· Here's a more generic approach based on a related post by Danny Simmons. I have confirmed that this works:

Code Snippet

public static EnduranceActivity UpdateEnduranceActivity(EnduranceActivity currentActivity, EnduranceActivity originalActivity, Route newRoute)

{

   using (FitnessAppContainer appContainer = new FitnessAppContainer(ConnectionString))

   {

      appContainer.Attach(originalActivity);

      ApplyReferencePropertyChanges(

               (IEntityWithRelationships)currentActivity,

          (IEntityWithRelationships)originalActivity);

      appContainer.ApplyPropertyChanges(

"FitnessAppContainer.EnduranceActivitySet",

currentActivity);

      appContainer.SaveChanges();

   }

   return originalActivity;

}

public static void ApplyReferencePropertyChanges(

IEntityWithRelationships oldEntity,

IEntityWithRelationships newEntity)

{

foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())

     {

   var oldRef = relatedEnd as EntityReference;

   if (oldRef != null)

   {

     // this related end is a reference not a collection

     var newRef = newEntity.RelationshipManager

     .GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName)

as EntityReference;

oldRef.EntityKey = newRef.EntityKey;

   }

}

}

· Hi,

I have used this method as part of a generic graph update method for my wcf services. However, it appears to ignore the reference values entirely. I can see that the reference properties are set in the method but when savechanges is called they do not persist?

When i test out workign with an always connected object graph (no wcf) the method works fine and the changes persist, any ideas?

On a side note, i've resorted to using dependency injection to have specific classes responsible for managing the updating of objects from wcf services as reflection was getting way too messy. I have the concept of allowing an udpate strategy key to be passed into the web service to instruct how the graph should be updated, with sensible defaults of course (association depth of 1 for example).

public void Update<T>(T entity, string updateStrategyKey)

{

EntityObject updatedEntity = entity as EntityObject;

// we're only interested in detached entities, any others will be updated by SaveChanges anyway

if (updatedEntity == null || updatedEntity.EntityState != EntityState.Detached)

return;

// get the current (old) entity from the object state manager using the updated entity

EntityObject oldEntity = GetObjectByKey(updatedEntity) as EntityObject;

// get context and relationship info about old and updated

ObjectContext objectContext = this.ObjectContext;

Type updatedEntityType = updatedEntity.GetType();

Type updatedEntityBaseType = GetBaseType(updatedEntityType);

// apply reference property changes (not handled by context's ApplyPropertyChanges)

ApplyReferencePropertyChanges(oldEntity, updatedEntity);

// apply scalar property changes

objectContext.ApplyPropertyChanges(updatedEntityBaseType.Name.ToString(), updatedEntity);

// if an update strategy is in place, use a dependency injected service to implement if available

if (!String.IsNullOrEmpty(updateStrategyKey) && updateStrategyKey != UpdateStrategyKey.RootOnly.ToString())

{

string repositoryGraphUpdaterServiceID = updatedEntityType.ToString() + "GraphUpdater";

IRepositoryGraphUpdater repositoryGraphUpdater = ServiceLocator.TryResolve<IRepositoryGraphUpdater>(repositoryGraphUpdaterServiceID);

if (repositoryGraphUpdater != null)

{

repositoryGraphUpdater.Update(updatedEntity, updateStrategyKey);

}

}

}

// apply reference property changes (not handled by context's ApplyPropertyChanges)

private void ApplyReferencePropertyChanges(IEntityWithRelationships oldEntity, IEntityWithRelationships updatedEntity)

{

foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())

{

var oldRef = relatedEnd as EntityReference;

if (oldRef != null)

{

// this related end is a reference not a collection

var newRef = updatedEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName)

as EntityReference;

oldRef.EntityKey = newRef.EntityKey;

}

}

}

· I've just been debugging this and it's the line in red below that doesn't appear to work. What happens for me is that the attached and detached entity keys appear from the old entity (attached) and the updated entity (yet to be attached) is ignored.

This is causing major problems for us as we can not seem to update any entity references as a result (as attach and attachto are not viaable options when protecting updates).

Let's say we have oldEntity below passed in as a customer with a null party reference as follows Customer.Party. The updatedEntity is a detached Customer with an updated party reference.

The problem then seems to be that we cannot use a detached updatedEntity when the oldEntity is attached and the reference you want to update on the attached entity is null, is this correct?

// apply reference property changes (not handled by context's ApplyPropertyChanges) oldentiy is attached and updatedentity not

private void ApplyReferencePropertyChanges(IEntityWithRelationships oldEntity, IEntityWithRelationships updatedEntity)

{

foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())

{

var oldRef = relatedEnd as EntityReference;

if (oldRef != null)

{

// this related end is a reference not a collection

var newRef = updatedEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName)

as EntityReference;

oldRef.EntityKey = newRef.EntityKey;

}

}

}

· aha!

I've just discovered that one must set the REFERENCE value on the client for many to ones. I assumed (wrongly) that you could set the property itself and that EF would set the reference on the way back!

It's not very intuitive however to have the following code, any ideas why this gets generated and not just PartyReference like on the entity class itself?

// update the customer's party

Party party = client.GetParty(7);

EntityReferenceOfPartyXwpgZWOi partyRef = new EntityReferenceOfPartyXwpgZWOi();

partyRef.EntityKey = party.EntityKey;

customer.PartyReference = partyRef;

I've finally gotten this working after days of struggle.

I've got a simple database of People and Departments:

ADO.NET Entity Framework Entity Data Model diagram with Department and Person objects

I can use strongly-typed ASP.NET MVC views for reference/navigation properties! See the list of departments...

ASP.NET MVC with DropDownList

Part of my Person/Edit view:

<% using (Html.BeginForm()) {%>
    <%= Html.Hidden("Id", Model.Id) %>
    <fieldset>
        <legend>Fields</legend>
        <p>
            <label for="Name">Name:</label>
            <%= Html.TextBox("Name", Model.Name) %>
        </p>
        <p>
            <label for="DepartmentId">Department:</label>
            <%= Html.DropDownList("DepartmentId", new SelectList((IEnumerable)ViewData["Departments"], "Id", "Name"))%>
        </p>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
<% } %>

Part of my Person controller:

//
// GET: /Person/Edit/5
public ActionResult Edit(Guid id)
{
ViewData["Departments"] = ctx.Department;
Person model = (from Person p in ctx.Person
where p.Id == id
select p).FirstOrDefault();
return View(model);
}
//
// POST: /Person/Edit
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Person model)
{
    ctx.AttachUpdated(model);  //extension
    ctx.SaveChanges();
return RedirectToAction("Index");
}

To get this working, I extended the Person EntityObject with a new DepartmentId property.

using System;
using System.Data;
using System.Data.Objects.DataClasses;
namespace ProjectName.Models
{
public partial class Person : EntityObject
    {
public Guid DepartmentId
        {
get
            {
try
                {
return (Guid)this.DepartmentReference.EntityKey.EntityKeyValues[0].Value;
                }
catch
                {
return Guid.Empty;
                }
            }
set
            {
this.DepartmentReference.EntityKey = new EntityKey("JunkEntities.Department", "Id", value);
            }
        }
    }
}

And I extended the Entity Framework ObjectContext with new AttachUpdated and ApplyReferencePropertyChanges methods:

using System;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
public static class EntityFrameworkExtensionMethods
{
public static void AttachUpdated(this ObjectContext ctx, EntityObject objectDetached)
    {
if (objectDetached.EntityKey == null)
        {
String entitySetName = ctx.DefaultContainerName + "." + objectDetached.GetType().Name;
Guid objectId = (Guid)objectDetached.GetType().GetProperty("Id").GetValue(objectDetached, null);
            objectDetached.EntityKey = new System.Data.EntityKey(entitySetName, "Id", objectId);
        }
if (objectDetached.EntityState == EntityState.Detached)
        {
object currentEntityInDb = null;
if (ctx.TryGetObjectByKey(objectDetached.EntityKey, out currentEntityInDb))
            {
                ctx.ApplyPropertyChanges(objectDetached.EntityKey.EntitySetName, objectDetached);
                ctx.ApplyReferencePropertyChanges((IEntityWithRelationships)objectDetached,
                                                  (IEntityWithRelationships)currentEntityInDb);  //extension
            }
else
            {
throw new ObjectNotFoundException();
            }
        }
    }
public static void ApplyReferencePropertyChanges(this ObjectContext ctx, IEntityWithRelationships newEntity, IEntityWithRelationships oldEntity)
    {
foreach (var relatedEnd in oldEntity.RelationshipManager.GetAllRelatedEnds())
        {
var oldRef = relatedEnd as EntityReference;
if (oldRef != null)
            {
var newRef = newEntity.RelationshipManager.GetRelatedEnd(oldRef.RelationshipName, oldRef.TargetRoleName) as EntityReference;
                oldRef.EntityKey = newRef.EntityKey;
            }
        }
    }
}

I just wanted to document my progress here. Please suggest improvements.


Posted 2009/5/31 1:17 by 革命
Filed under: ,