Page History: Saving Hierarchies with RIA Services
Compare Page Revisions
Page Revision: Monday, 19 July 2010 03:52 AM
The following originally came from a post on the RIA Services forum. The original thread can be found at http://forums.silverlight.net/forums/t/191660.aspx.The RIA Services client is made up of several objects that include the DomainClient, DomainContext, EntityContainer, EntitySet, EntityCollection, and EntityRef.
The object that actually hold the entities and knows how to add and remove them is the EntitySet. The EntitySets are owned by the EntityContainer (that is pretty much all that the EntityContainer does) and the EntityContainer is owned by the DomainContext.
Associations between entities are managed by the EntityCollection (the Many side of an association) and the EntityRef (the One side of an association). The two act as "views" into the related EntitySet. So, for example, if you have ObjectA which has ID and ObjectB which has an ObjectAId field with the appropriate association tag setup then ObjectA has an EntityCollection object which is scanning the ObjectB EntitySet for any ObjectBs that have a matching ObjectAId field on them. Every time an ObjectB is added, deleted, or updated the ObjectA EntitySet is rechecking to see if that added, deleted, or modified ObjectB should be displayed in ObjectA's EntityCollection.
OK, with all of that back story in place, why does adding an object to the EntityCollection actually insert a new Object but removing it does not? Simply put, the Add behavior is a side effect behavior and not the intended behavior. This makes it confusing. Remember, the association's are really based around the value of foreign keys, not really adding and removing objects from a collection. So, adding an entity to the collection really means "Set the foreign key value of this entity equal to the primary key of the owning entity". However, if you are adding an entity which is currently detached to the collection that entity must be attached to its EntitySet for the EntityCollection to find it after the foreign key has been set. So, the Add has been designed to go ahead and do that for you, it adds it to the EntityCollection if it is currently detached. That is why I call it a side effect.
The EntityCollection.Remove on the other hand only does a "Set the foreign key value of this entity to NULL" behavior, it doesn't remove the entity from its EntitySet. That is why you are not seeing a delete happening if you just remove from the EntityCollection. The logic is that EntityB could have a nullable foriegn key or you may be wanting to write code like the following:
EntityAInstanceOne.EntityBs.Remove(currentEntityB);
EntityAInstanceTwo.EntityBs.Add(currentEntityB);
OK, now for the EntitySet. There are two ways to access the EntitySet. One way is that any Entity directly exposed through a query method gets a property on the DomainContext that directly exposes it by name. So, if you looked at the generated code of the DomainContext (an activity which I strongly suggest for proper understanding) you should see a property that looks something like this:
public EntitySet<ORDER> Orders
{
get
{
return base.EntityContainer.GetEntitySet<ORDER>();
}
}
Now, I personally tend to just leave the stubs in place on the DomainService so that I get all of those, but if you don't want to do that you can always get the EntitySet yourself using the same type of code:
EntitySet
orderLines = yourDomainContext.EntityContainer.GetEntitySet();
...or just add the properties to the DomainContext yourself through a partial class.
Last part of the lesson: Composition. What composition does is hide the ObjectB EntitySet completely (it still exists, you just aren't supposed to access it directly), changes the EntityCollection so that the Remove does delete the entity from the EntitySet, and changes several other behaviors. The short term upshot is that you don't need to understand the whole EntityCollection, EntityRef, EntitySet system. The long term problem is that you lose a lot of flexibility and performance. That is why I always recommend that people avoid using composition unless they really need it. You need composition if you have business rules on the parent that depend on values in the child or business rules on the child that depend on the parent, that is why the feature was added to RIA Services.