I have not been able to stop thinking about EntityKeys, serialization and web services since my post about them yesterday, so I have done a little more experimentation tonight.
ObjectContext does have an AttachTo method. In my scenario of the incoming object from a web service client, the EntityKey is not available as part of the SalesOrderHeader entity because that is currently not being serialized as part of the entity. Note that this is a bug that the Entity Framework team is aware of and will fix.
While Attach allows you attach an entity that has a key, AttachTo does not require the EntityKey. However it does require that you know the name of the EntitySet.
In the type of testing scenario that I am working with , this is not a big problem.
While Attach would let me call
AttachTo has me call
Once I do this, the EntityKey is created.
Note the contents of an entity key:
It has the name of the EntitySet in it. (I did not fix up the EntitySet names that the wizard created. I should have done this right after the wizard, and the new designer makes that really easy to do now.) That should be SalesOrderHeaders (plural)).
The EntitySet is nowhere else in the definition of my “incoming” SalesOrderHeader.
So if I didn’t happen to know the name of the EntitySet, I wouldn’t be able to attach it. If I want to have a generic updater, this could be a problem.
And a word or two hundred about concurrency:
The EntityKey itself has no impact on how to handle updates in the service.
As with LINQ to SQL, when I attach an object, it attaches as an unmodified entity. SaveChanges does nothing. The entity keeps track of original and modified values (which you can actually see if you create an ObjectStateEntry object from the entity). I actually tried attaching another instance of the object with different values, then attaching my incoming one to see if the 2nd instance would be seen as modified. No go. (Yah – grabbing at straws…and random ones at that!)
I tried calling Refresh with the ClientWins parameter (along with an array that contained my entity) to see if I could get the database to push it’s original values into the entity and somehow get the incoming values to magically become modified values. There have to be modified values to begin with for any of this to work. That doesn’t change anything because the state of the entity is still unmodified since the time I attached it.
The next thing I tried was updating the ModifiedTime property to see if kicking the ModifiedState of the entity would force all of my changes to get saved. Only ModifiedTime was saved, not the other values that were different.
The last thing I tried was, after attaching the entity, change the value using this funny method:
AND IT WORKED!
The entityState became modified and the Comment column was updated in the database.
So at this point, it still looks like there is no way around explicitly updating the entity properties in order to get them to be seen as modified. But I have no way of knowing WHICH of these things have been changed. Which leaves me a few options (probably more than just these three):
- Update every property
- Cache the entities when they are retrieved from the database (though I have to figure out what I want to cache… a dataset? an entire ObjectContext? … then I have to make decisions about keeping that refreshed).
- Grab the current data from the database just before I want to update. There are also potential concurrency issues with this as well.
- Iterate through each property and, using the ObjectStateEntry, set them as modified using the SetModifiedProperty, which takes the property name (string) as a parameter. I could do this in conjunction with data from the database or data from some cached store and only do this to properties that are different.
However all of these options seem to rely on knowing the name of the properties and I have not been able to find a way to programmatically discover them.
Therefore, it seems that I may have to go down one of these paths:
- Use reflection to iterate through all of the properties and update them. My feeling about having to go to these lengths is “just no!” Plus this will mean that every property will be seen as modified and will get updated.
- Write updaters for every entity that will explicitly set each property. That’s a crap load of data access code and not having to write a crap load of data access code is one of the benefits of Entity Framework.
- Hope and pray that the EntityFramework team can somehow give us a way of attaching an object for original and one for modified. LINQ to SQL has this.
- Wait for someone on the team to comment in my blog and show me the super easy obvious way that I have totally overlooked.
What’s a little funny to me is that Rick Strahl has had a similar conundrum with LINQ to SQL.
And I’m still only talking about full-on clientWins concurrency, so far.
Sign up for my newsletter so you don't miss my conference & Pluralsight course announcements!