locked
Composition Relationship Design Advice RRS feed

  • Question

  • I have 2 classes:  SalesOrder and LineItem

    here are the members:

    LineItem.Price

    SalesOrder.LineItems

    SalesOrder.Total -> This is a derived property that sums the LineItem.Price properties in the collection

    I'm thinking that to achieve this behavior, I should create a composite relationship.  Then SalesOrder would need to know whenever LineItem.Price changes to raise an event (INotifyPropertyChanged.PropertyChanged("Total")).  At this point, I'm only assuming that there are hooks available for SalesOrder to know that any of its children's properties changed.

    Does anyone know if this is the only real practical design given WCF RIA's architecture?  Has anyone else implemented a similar design and if so, how?

    Thanks for your thoughts!

    Friday, April 30, 2010 4:55 PM

Answers

  • I've written this same logic before, but have focused it on the INotifyCollectionChanged and INotifyPropertyChanged interfaces so I could regularly reuse it. From my experience with it in the past, I think it often works best implemented in a class separate from the Entity. If you take a look at the DomainDataSource (source), it uses a collection manager that listens to item property changes and collates those events into an interface that is simple for the DDS to consume.

    Kyle

    Wednesday, May 5, 2010 12:21 PM

All replies

  • Is WCF RIA too new for anyone to have hit this design scenario?  It seems like a pretty common design pattern to me. 

    Are there any other designs that anyone has seen or come up with that achieves the same behavior?

    I'm just thinking that not completely having my head around the WCF RIA architecture is limiting my design options and keeping my designs narrowly focused.

    I would like to have more options based on WCF RIA's architecture.

    Thanks again!

    Monday, May 3, 2010 3:16 PM
  • I used the composite pattern, but what I did was build up the tree in xml using the pattern and then send the xml to the client via RIA services. That way I had a structured format on either side of the wire to play around with.

     Hope that helps.

    Monday, May 3, 2010 3:34 PM
  • Yes, you can use composition to help achive that.

    Monday, May 3, 2010 3:49 PM
  • Thanks for the replies thus far.  It turns out that my design will not work as I had hoped.  In my SalesOrder class, I have overriden OnLoaded like so:

     public partial class SalesOrder
     {

             protected override void OnLoaded(bool isInitialLoad)
             {
                 base.OnLoaded(isInitialLoad);
                 MyDomainContext context = (MyDomainContext)Application.Current.Resources["MyDomainContext "];
                 context.LineItems.PropertyChanged += (s,e) => RaisePropertyChanged("Total");
             }

             public double Total
             {
                 get { return LineItems.Sum(i => i.Price); }
             }
     }

    So SalesOrder is simply waiting to hear from a change in the LineItems collection exposed via the DomainContext.  This works one time and one time only.  When an item's property in the LineItems collection changes, LineItems.PropertyChanged fires. 

    However, any other changes to any LineItem in the collection does NOT fire LineItems.PropertyChanged until SubmitChanges() is invoked.

    Unless I'm not getting something, it appears that I will not be able to have a calculated field in my SalesOrder class based on its childrens properties changing.  This is a real disappointment.

    Am I missing something here?

    Monday, May 3, 2010 11:05 PM
  • is not because thw context.LineItems.PropertyChanged happeneds in the OnLoaded event? Have you tried to raise the event from a button/mouse action?

    Monday, May 3, 2010 11:51 PM
  • Do you mean did I try to invoke RaisePropertyChanged() from a button click handler?  Like, as a test to verify that it is indeed firing?

    Otherwise I'm not sure what you're suggesting.

    I'm actually thinking that LineItems.PropertyChanged is firing once by WCF RIA design, although I hope that this is not the case.

    Any other thoughts?

    Tuesday, May 4, 2010 9:08 AM
  • Would someone from the WCF RIA team be so kind as to chime in here?  Because I cannot subscribe to a PropertyChanged event of an individual LineItem, I have subscribed to the PropertyChanged event of the context.LineItems.

    But again, it only fires once.  So if I tab through a grid bound to these items, changing their .Price values, the UI is only notified on the first item change, and the rest of the changes are ignored.

    Any thoughts would be greatly appreciated!

    Tuesday, May 4, 2010 3:07 PM
  • The PropertyChanged event fires when a value changes on the EntitySet itself, not on its items. In this case, the one event you're seeing is probably for "HasChanges". To implement this correctly, you need to listen to collection changed events on the EntitySet and listen to property changed events on each item in the EntitySet. It's a pain to write, but that's the only way to get correct event propagation.

    Kyle

    Wednesday, May 5, 2010 11:20 AM
  • Thanks Kyle, I figured as much.  So basically I will need to create my own EventHandler (eg. LineItemChanged) in the LineItem partial class. 

    Then in order for the SalesOrder to be properly notified when a LineItem changes, I'll need to override SalesOrder.OnLoaded() and do the following:

    1.) Get the DomainContext, loop through all of the LineItems, and register to handle the custom LineItemChanged event.

    2.) Register to handle the LineItems.EntityAdded event.  When this event is handled, register to handle the new LineItem.LineItemChanged event.

    3.) Register to handle the LineItems.EntityRemoved event.  When this event is handled, remove the LineItem.LineItemChanged event.

    This is a complete mess.  What's worse is that because all classes inherit from Entity, this is not functionality that can be baked into the framework and reused.  Therefore, this same functionality will have to be duplicated in each class where I want this type of notification behavior.

    Am I missing something here?  I hope so, I really appreciate the code generation abilities that WCF RIA provides as it saves a TON of time. 

    However, I'm finding that I constantly have to jump through hoops the further down the road I venture...

    Wednesday, May 5, 2010 11:55 AM
  • I've written this same logic before, but have focused it on the INotifyCollectionChanged and INotifyPropertyChanged interfaces so I could regularly reuse it. From my experience with it in the past, I think it often works best implemented in a class separate from the Entity. If you take a look at the DomainDataSource (source), it uses a collection manager that listens to item property changes and collates those events into an interface that is simple for the DDS to consume.

    Kyle

    Wednesday, May 5, 2010 12:21 PM