Thursday 20 November 2008

Why Support One-Way Multiple Associations?

This post is to answer a question from the Developer Express support team as to why I have requested that XPO support one-way multiple associations. The primary reason for my request is to reduce code noise. When you do not want a reverse association in your model, then you should not be forced to put it in. Using the example from my previous post, if all you need is a one-way association you should be able to write:

  public class Customer : XPObject
  {
    public XPCollection Orders { get; }
  }
  public class Order : XPObject
  {
  }

However, as XPO currently requires you to implement a two-way association in your object model. You have to write something like this:

  public class Customer : XPObject
  {
    [Association("CustomerOrders")]
    public XPCollection Orders { get; }
  }
  public class Order : XPObject
  {
    [Association("CustomerOrders")]
    public Customer Customer;
  }

XPO has made me:
  • Add an attribute to my association property.
  • Give that attribute a unique string to identify the association.
  • Add a new property to my linked type.
  • Add an attribute to this new property.
  • Give this attribute the same string so as to identify it is part of the association.
I have no problem with doing that when I need the association to be two-way. Indeed, XPO does some good things for me when I do need this: it maintains the linked states of the linked instances, which is a great help! In other words, when the Association attribute is used as above, adding an order to a customer's orders will automatically set the order's customer; removing the customer from the order's customer property will remove the order from the customer's orders property. Very nice to have and certainly removes code that I would need to write.

You are probably looking at the above example and thinking that you would naturally have an association from order to customer, and I would agree. However, let's say that you have a requirement to show the recently ordered products against the customer. One way of implementing this would be:

  public class Customer : XPObject
  {
    public XPCollection RecentProducts { get; }
  }

In this circumstance, I would not want to have to implement a reverse association:

  public class Customer : XPObject
  {
    [Association("CustomerRecentProducts")]
    public XPCollection RecentProducts { get; }
  }

  public class Product : XPObject
  {
    [Association("CustomerRecentProducts")]
    public XPCollection Customers { get; }
  }

In this case I would consider the reverse association to be worse than code noise - it is structurally worng and simply should not be there! Maybe we should be suspicious of the Customer.RecentProducts association in the first place too, but that's another story!

Another time when being forced to implement a reverse association is when the two classes are in different assemblies. I would have to move the classes to avoind a circular reference. Maybe I should ask for XPO to support persistence by interface too...


No comments: