Поделиться через


Composition Support in RIA Services

I’m having a great time at PDC 2009 talking to customers about Silverlight and RIA Services. The second keynote just finished, Scott unveiled SL4 which means now our team can talk about our PDC Beta of RIA Services for VS 2008 / SL3 as well as our preview for VS 2010 / SL4! You can download the new bits from our WCF RIA Services landing site.

Between sessions, I've found time to make the first of a series of posts on the new features and work we’ve done in this release. In this post I’ll discuss the support we’ve added to RIA Services for compositional relationships. Lets jump right in :) A compositional relationship has the following characteristics:

  • the lifetime of the child of the association is governed by the parent
  • the parent and its children are treated as a single unit
  • children have no identity/existence independent of their parent

The classic PurchaseOrder/OrderDetails association has these characteristics and is often modeled as a composition. A detail cannot exist independent of its parent and generally when operating on an order it and all its details are viewed together and operated upon / validated together. In contrast, the Employee / Reports association is not a compositional – reporting employees exist independent of their manager, meaning if the manager is fired, the reports are hopefully re-parented :)

To facilitate application models that include compositional relationships, we’ve introduced CompositionAttribute which can be used to mark an association as a composition. Of course you can have compositional hierarchies of arbitrary depth. Compositions in RIA Services gain the following behaviors:

  • Hierarchical change tracking – when a child entity is modified, it’s parent also transitions to the Modified state.
  • When a parent is in the Modified state, all of its children are included in the change-set sent to the server, including any Unmodified children.
  • Operation ordering – Often only CRUD operations for the parent or root type in a compositional hierarchy will be exposed by a DomainService. This allows you to write all your business logic for a hierarchy in a single method. However writing explicit methods for child Types is supported, in which case parent operations are always called before child operations. For example, if a new OrderDetail was added to an existing PurchaseOrder, the Update method for PurchaseOrder would be called before the Insert for the OrderDetail.
  • Public EntitySets for child Types are not generated on the code-genned DomainContext. Children are only accessible via their parent relationship.

Simply marking an association with the attribute enables all this functionality automatically for the association:

    1: public class PurchaseOrderMetadata
    2: {
    3:     [Include]
    4:     [Composition]
    5:     public EntitySet<OrderDetail> OrderDetails;
    6: }

 

In addition to this new attribute there are a few new server side APIs to help with compositions. These new APIs are shown below in a hierarchical update method that takes a PurchaseOrder / OrderDetail compositional hierarchy and controls the update for the entire hierarchy:

 

    1: public void UpdateOrder(PurchaseOrder order)
    2: {
    3:     PurchaseOrder origOrder = this.ChangeSet.GetOriginal(order);
    4:     if (origOrder != null)
    5:     {
    6:         this.DataContext.PurchaseOrders.Attach(order, origOrder);
    7:     }
    8:     else
    9:     {
   10:         this.DataContext.PurchaseOrders.Attach(order);
   11:     }
   12:  
   13:     foreach (OrderDetail detail in
   14:       this.ChangeSet.GetAssociatedChanges(order, p => p.OrderDetails))
   15:     {
   16:         ChangeOperation op = this.ChangeSet.GetChangeOperation(detail);
   17:         switch (op)
   18:         {
   19:             case ChangeOperation.Insert:
   20:                 this.DataContext.OrderDetails.InsertOnSubmit(detail);
   21:                 break;
   22:             case ChangeOperation.Update:
   23:                 this.DataContext.OrderDetails.Attach(detail, 
   24:             this.ChangeSet.GetOriginal(detail));
   25:                 break;
   26:             case ChangeOperation.Delete:
   27:                 this.DataContext.OrderDetails.Attach(detail);
   28:                 this.DataContext.OrderDetails.DeleteOnSubmit(detail);
   29:                 break;
   30:             default:
   31:                 break;
   32:         }
   33:     }
   34: }

 

The new APIs shown above are:

  • ChangeSet.GetAssociatedChanges – given a parent object and an association, this method returns all child objects in the change-set for that association. You could simply enumerate PurchaseOrder.OrderDetails directly, but of course that wouldn’t include deleted Details. The helper method returns those as well.
  • ChangeSet.GetChangeOperation – for a specified object, returns a ChangeOperation enum value indicating whether the object operation is Insert, Update, Delete, or None (unmodified).

This post has been a quick introduction to the feature. Attached is a zipped project containing a fully functional composition sample (VS 2010). The solution includes a detailed readme which walks you through the details of the sample and demonstrates the feature in more depth. Now I’m going to go down to the Silverlight booth to chat with more users :)

CompositionSamples.zip

Comments

  • Anonymous
    November 29, 2009
    Hi Mathew, I'm desperate for a simplier (or modified) example of composition using LinqToEntitiesDomainService. Do you have something lying around you could share with me? Note that I also noticed some strange behaviour with the changeset for the parent object. When, adding a child object, the parent is marked as modified in the changeset but a call to GetOrginal returns null. Regards, Mark.

  • Anonymous
    November 29, 2009
    Oop, sorry I meant similar not simplier (which isn't event a word).

  • Anonymous
    November 30, 2009
    Mark, the translation of this sample to EF should be straight forward - you'll simply have to call the EF attach/CUD operations corresponsing to the ones you see in the sample. For example Attach/AttachAsModified, AddXXX, DeleteObject, etc. Let me know if you run into any issues. Regarding GetOriginal for an unmodified parent - this is by design. Only if the parent has actual property changes will there be an original. If it's unmodified, current IS original :)

  • Anonymous
    December 01, 2009
    Hi Mathew, I was able to get UD operations working. There seems to be a problem with adding child objects as when the parent entity is attached, it also attaches all other entities in its graph. Because of this, when I call AddToXXX based on an insert operation of the child entity, I get the following exception: "Submit operation failed. An object with the same key already exists in the ObjectStateManager. The existing object is in the Unchanged state. An object can only be added to the ObjectStateManager again if it is in the added state." I would be happy to share the code I have written with you although I'm not sure the best way to do this? (It is almost identical to your's however except for using EF of course). I don't feel like I'm doing anything obviously wrong however those are famous last words. I guess this maybe a stupid question, but has this funcationality been tested with EF? Regards, Mark.

  • Anonymous
    December 01, 2009
    Mark, when dealing with EF eager attaches, you'll have to use the right EF APIs to transition entity state properly. For example, if the child entity state is not Detached when you need to Add it, you'll need to use ObjectStatemanager.ChangeObjectState to move it to the Added state. Take a look at the code our DomainService Wizard generates for Insert methods for an example. The link I provided to the new EF features will give more details on using EF in N-Tier scenarios. To answer your testing question, of course this has been tested with EF :) We run the exact same set of tests against both our LTS and EF providers.

  • Anonymous
    December 01, 2009
    Thanks Mathew, I thought I would need to do something like this although I couldn't work out how to do it. Of course I'm using .Net3.5 and SL3 so I guess I need to install the betas to get this to work? Regards, Mark.

  • Anonymous
    December 02, 2009
    Yes, this post is specific to the 2010 / .NET 4.0 RIA Services release. However, if you're using the beta (w/o the new EF APIs), you might still be able to get around your issue by first calling Detach on the entity and setting it's EntityKey to null before adding it.

  • Anonymous
    December 02, 2009
    Ok thanks, I'll give that a go. BTW, everything you have said made alot more sense after I read your latest posting. Thanks for you help. Regards, Mark

  • Anonymous
    April 06, 2010
    Could  you provide some working code for EntityFramework? We tried a few days figure out how to do it with no success. Our project is stopped until we resolve this issue.

  • Anonymous
    April 12, 2010
    This is great news for RIA devs... But where is the matching InsertOrder method? In order to update something, you need first put it in the db... How has this changed with the Composition attribute?

  • Anonymous
    April 12, 2010
    [VS 2010 RC + SL 4.0 + EF] I also have the problem with updating MasterObject with changes to DetailObjects (the same relation as in Order -> OrderDetails). Could you please provide some example working with entity framework? If it helps I can prepare some example which doesn't work... :)

  • Anonymous
    June 03, 2010
    Hi Matthew! Haven't found anywhere a sample which shows how to handle the deletion of a master record. How should this be handled? It seems to be a very big headache (at least to me). I have this situation where i want to delete the master record (which has some associated details records): this results in a foreign key violation (may be because all the CUD operations are done on the master record first, as the documentation states). Should this be solved by forcing the dbms to use a cascade on delete? is there any other way? Could you please provide a sample? Regards Savvas PS. Using VS2010, EF

  • Anonymous
    June 24, 2010
    Hello, This is the only ressource on the web about composition... Everybody quotes you ;) What I want to do is display (and edit) the properties of both the entities of the association in the same grid row. How can this be done? The only thing I'm able to do is display OrderDetails.Count. How do I bind to the other properties of OrderDetails? That would help me a lot as I'm stuck exactly here... Thanks...

  • Anonymous
    June 26, 2010
    Matthew, Composition is a potentially useful feature. I've spent several days going round in circles trying to make it work with EF without success. I get around one problem only to be confronted by another one. You can't just toss code like this over the fence and expect developers to use it without really good documentation. As it is, I suspect the code is broken despite your protestations about testing. Whatever the case, it doesn't relieve the need for documentation. Sorry for the angst but I am sooo frustrated by this. Here are some of the things I think I need:

  1. A description of what the framework does and roughly how. This is essential as I need to write code (the update methods) that work with the framework. It should describe assumptions you make about how data is treated on the client, and any processing that entities are subjected to (ie anything that would change any aspect of entity state).
  2. A description of entity states and change states, what they mean, how they are treated by the framework, and when/how I should manipulate them in typical RIA scenarios.
  3. Examples of how to manipulate hierarchies in typical entity Insert/Update/Delete methods, together with reasons why each such manipulation is needed.
  4. Working sample code. Is there any example code you can point to (EF)? The last of these is probably the most urgently needed, but I'm sure others can add to the list. Dave.
  • Anonymous
    June 29, 2010
    Sorry, I see this post has generated a lot of questions that I haven't answered. I'll try to respond to some of the above comments here: LizardIsland : I've added a new file to the CompositionSamples zip - "EFCompositionSample.cs", which is an EF example stolen from our unit tests. As you'll see (and have discovered yourself) that things are not entirely straightforward when using EF with composition. The EF change tracker has some very specific rules about the order in which changes must be played back - you'll see that in the sample. Regarding understanding the feature in general, hopefully the MSDN doc answers most of your questions (msdn.microsoft.com/.../ee707346(v=VS.91).aspx). John : Not sure I understand. I recommend posting your question to our forum (forums.silverlight.net/.../53.aspx) Savvas : The EF sample I uploaded should help answer your question. When deleting the master, you need to be sure to delete the children as well, and any other entities with FK references to things you're deleting.

  • Anonymous
    August 04, 2010
    Given that MS is now delivering a lot of new code, and faster than the technical writers could ever keep up with, how about releasing sources of the unit-tests so that we can better understand the scenarios? After all, we are all consenting adults, anything that helps new code get adopted must be a good thing for all.

  • Anonymous
    August 26, 2010
    By the way, for the benefit of other readers. If the information on this post didn't quite work, the following one did the trick: social.msdn.microsoft.com/.../a633a58e-85ad-4e77-be80-b1855bfd2b42

  • Anonymous
    February 13, 2011
    Hi All! I know that this is some old blog entry but I still have major problems with this "Composition" stuff. The given examples all work perfect - as long as you only add ONE child at a time. As soon as you add more than one child the examples are no longer working. When "attaching" the parent entity to the object context in the "UpdateEntity" function I get an error sayin that this entity is already added to the ObjectStateManager. Anybody got a solution for this???

  • Anonymous
    February 14, 2011
    OK, I found the solution to the problem of adding more than one child to a parent. Initially every child has an Id of 0 (zero). So when adding the parent entity to the ObjectContext there are 2 or more child entities with the same key of zero generating an error message. The solution is to give every child entity in Id of non-zero. On the client-side I extended the partial class of the parent entity by adding a private tempId variable which gets incremented by 1 each time a child entity is added to the parent. For this I extended the parent entity by a function like "AddOrderDetail(OrderDetail detail)..." and set the OrderDetailId to the tempId thus avoiding the error message on the server side. Hope my english is good enough to be understood... Greets from Germany Heiko