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