Relations and custom collections

XPO Team Blog
07 April 2006

A recent discussion in the newsgroup prompted me to summarize the important things to know about creating relationships between classes, using collection type properties, and how to use derived collection types instead of the standard ones.

One-to-many relationships

A one-to-many relationship needs at least the reference on the “many” side. As an example, consider two classes Artist and Album. The Album class must have this:

  public class Album : XPObject {
    ...
    Artist artist;
    [Association("Artist-Albums")]
    public Artist Artist {
      get { return artist; }
      set {
        if (artist != value) {
          Artist oldValue = artist;
          artist = value;
          OnChanged("Artist", oldValue, artist);
        }
      }
    }
    ...
  }

On the other side of the relation, the collection type property is optional. You'll normally add it for one of three reasons:

  1. You actually need the direct access to all the albums of an artist.
  2. You like the the neatness of having everything defined completely.
  3. You want to aggregate the collection by using the AggregatedAttribute, which works only on the "one" side of the relation.

The code on the “one” side looks like this:

  public class Artist : XPObject {
    ...
    [Association("Artist-Albums", typeof(Album)]
    public XPCollection Albums {
      get { return GetCollection("Albums"); }
    }
    ...
  }

There are two interesting variants of the syntax above:

  1. If you use XPO 6.1 with VS 2005, you can use the generic variants of the XPCollection and the GetCollection method. In turn, you can leave out the type parameter of the AssociationAttribute, because the type is then inferred by the generic type attribute. This is how it would look:
      public class Artist : XPObject {
        ...
        [Association("Artist-Albums"]
        public XPCollection<Album> Albums {
          get { return GetCollection<Album>("Albums"); }
        }
        ...
      }
    
  2. To aggregate the collection, you could add the AggregatedAttribute on the collection property.

Many-to-Many relationships

Based on the above, many-to-many relationships are very easy to explain: they need a collection type property on both ends, each of them similar to the one described for the “one” side of the one-to-many relationship.

Custom collection types

Sometimes there might be reasons to derive custom collection types from the standard XPCollection or the XPCollection<T>. This is a lot easier to do in XPO 2006 than in XPO 1 because only one such type (if we neglect the “standard” vs. the generic variant for a moment) is used, i.e. the GetCollection method always return an XPCollection and GetCollection<T> always returns XPCollection<T>. In XPO 1 this used to be different, in that GetCollection would return XPOneToManyCollection or XPManyToManyCollection, depending on the relation type of the property – so the developer was forced to create three different derived types to cover all eventualities.

The standard types XPCollection and XPCollection<T> have a special constructor that is used for the case where they must be initialized as relationship collections. This specific collection constructor is relevant in two ways:

  1. You must make sure that your derived collection type implements it. This is not difficult, because you just need boilerplate code to pass through the parameters to the base class constructor, but you must not leave it out.
  2. In relation properties on the “one” side of a one-to-many relationship, or in the properties on both sides of a many-to-many relationship, you must use this constructor to create the collection of your derived type. This is also boilerplate code, but you must get it right.

So, assuming you had a derived collection class MyCollection<T>, this might look similar to this code (of course there might be other constructors and probably other code that implements your specific functionality):

  public class MyCollection<T> : XPCollection<T> {
    ...
    public MyCollection<T>(Session session, object theOwner, XPMemberInfo refProperty) :
      base(session, theOwner, refProperty) { }
    ...
  }

And to use this collection type in relation property, you’d have to write your code like this:

  public class Artist : XPObject {
    ...
    MyCollection<Album> myCollection;
    [Association("Artist-Albums"]
    public MyCollection<Album> Albums {
      get { 
        if (myCollection == null)
          myCollection = new MyCollection<Album>(Session, this, ClassInfo.GetMember("Albums"));
        return myCollection;
      }
    }
    ...
  }

The code in the property getter is essentially the same code that's contained in the GetCollection<T> method – but because that method doesn’t return our own collection type we have to replace it.

References

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.
Tags
No Comments

Please login or register to post comments.