views:

368

answers:

1

Self tracking entities. Awesome.

Except when you do something like

return Db.Users;

none of the self-tracking entities are tracking (until, possibly, they are deserialized).

Fine. So we have to recognize that there is a possibility that an entity returning to us does not have tracking enabled.

Now what???

Things I have tried

For the given method body:

using (var db = new Database())
{
    if (update.ChangeTracker.ChangeTrackingEnabled)
        db.Configurations.ApplyChanges(update);
    else
        FigureItOut(update, db);

    db.SaveChanges();
    update.AcceptChanges();
}

The following implementations of FigureItOut all fail:

db.Configurations.Attach(update);
db.DetectChanges();

Nor

db.Configurations.Attach(update);
db.Configurations.ApplyCurrentValues(update);

Nor

db.Configurations.Attach(update);
db.Configurations.ApplyOriginalValues(update);

Nor

db.Configurations.Attach(update);
db.Configurations.ApplyChanges(update

Nor about anything else I can figure to throw at it, other than

  1. Getting the original entity from the database
  2. Comparing each property by hand
  3. Updating properties as needed

What, exactly, am I supposed to do with self-tracking entities that aren't tracking themselves??


Small update:

Blindly marking the entity as modified works, however this seems a bit smelly. Is it the best we can do in this case?

+3  A: 

scenario 1

Here are some recommended practices to follow. When you are using STE in WCF scenario, you should rely on the change tracker that STE implements so on the server side you do the following.

db.Users.ApplyChanges(user);
db.SaveChanges();

scenario 2 However if you are on the server, the recommended practice is to create a method on the partial class for objectcontext called EnableChangeTracking. The method would query for entities that are in unchanged state that implements IObjectWithChangeTracker and turns on change tracking so something like this

user = db.users.first(u => u.userid == 1);
db.EnableChangeTracking();

now try to save the user entity from a different context from which it was originally retrieved from

db2.users.ApplyChanges(user);
db2.SaveChanges();

scenario 3 if on the server side you are connected to the same object context from which you retrieved the user entity from, then you use STE as simple poco object like below

user = db.users.first(u => u.userid == 1);
user.LastName = "XYZ";
db.DetectChanges(); //no need for it cuz Savechanges implicitly calls this.
db.SaveChanges();

scenario 4 if the user entity is retrieved from a different context then the context u will use it to save then here is another option where u mark the entity as modified and not care what got modified.

user = db.users.first(u => u.userid == 1);
var db2 = new ObjectContext();
user.LastName = "XYZ";
db2.Users.Attach(user);
// i prefer this option..
db2.ObjectStateManager.ChangeObjectState(user,EntityState.Modified); 
db2.SaveChanges(); // updates all columns

scenario 5 if the user entity is retrieved from a different context then the context u will use it to save then here is another option where u retrieve the original entity.

user = db.users.first(u => u.userid == 1);
user.lastName ="XYZ";
var db2 = new ObjectContext();
db2.Users.First(u => u.userid == user.userid);
db2.users.ApplyCurrentValues(user);
db2.SaveChanges();

Here is a blog post that describes few scenarios. http://weblogs.asp.net/zeeshanhirani/archive/2010/03/30/modifying-self-tracking-entity-on-the-server.aspx

I extensively cover these concepts in my Entity Framework 4.0 recipes book with lots of scenarios..

zeeshanhirani