Building on our past discussion (see Improving on POCO Properties and Improving on POCO Collections), let us look at how we can improve the implementation of associations between our domain objects.

In a standard POCO domain model, associations are handled as simple object references, or as lists of object references:

class Customer
    Person PerformedBy;
    IList<Orders> PlacesOrders;

With this approach, you either need to use eager loading and bring everything into memory at once (not usually a good idea, as often you end up loading far too much, with a significant cost in memory and time), or somehow force lazy-loading into play. NHibernate provides near transparent support for lazy-loading by requiring all public methods and properties to be virtual and then creating a descending proxy class at runtime. While not ideal, it’s one approach that works well within the limitations of POCO classes. A specialist helper class, however, could elevate lazy-loading to a first class concept, one natively supported by the domain layer.

Introducing helper classes to manage these associations, our Customer class declaration would look like this:

class Customer
    Reference<Person> PerformedBy;
    ReferencesMany<Orders> PlacesOrders;

As before, this becomes a lot more interesting when we start looking at our helper classes. Here’s the Reference<T> class, used when the association is single-valued.

class Reference<T>
    bool Modified;
    bool Loaded;
    T Instance;
    T OriginalInstance;
    void Commit();
    void Revert();
    event EventHandler<InstanceChangingEventArgs> InstanceChanging { add; remove; }

The Loaded property allows for code to check if the association has already been loaded, and perhaps to avoid accessing the association. In common with our previous examples - the helper classes (Property)[/blog/201001/improving-poco-properties] and (DomainCollection)[/blog/201001/improving-poco-collections] - we have support for both dirty checking and transactionality.

For multi-valued associations, here’s the ReferencesMany<T> class:

class ReferencesMany<T> : IList<T>
    IEnumerable<T> OriginalInstances { get; }
    bool Loaded;
    bool Modified { get; }
    void Clear();
    void Commit();
    void Revert();
    void Size();
    event EventHandler<InstanceAddingEventArgs> InstanceAdding { add; remove; }
    event EventHandler<InstanceRemovingEventArgs> InstanceRemoving { add; remove; }

As we’ve seen before, the Loaded property allows code to check to see if the association has been loaded into memory before doing anything that would require a load. We have support for both dirty checking and transactionality; and events provide a way for the parent instance to exert control over what happens to the association.

One special case that it’s useful to consider is the need to know how many instances are at the end of the association

  • as when performing checks before deletion. For performance reasons, it can be useful to be able to find this out directly from the database and without forcing the association to load. To support this, the Size() function counts the number of matching rows directly in the database. Code accessing the association has the choice of forcing a Load, by accessing Count(), or bypassing it, by calling Size().

Using helper classes to represent domain associations gives your code a richer base, making it easier to express your business rules. A useful side effect is the additional information now available for an ORM to use when making database updates.


blog comments powered by Disqus
Next Post
Sometimes, the answer is simple  25 Jan 2010
Prior Post
Dude, you're working too hard  20 Jan 2010
Related Posts
The Liskov substitution principle goes both ways  21 Mar 2020
Avoid hardcoded wait times  26 May 2018
Guaranteed Progression  19 May 2018
Prefer declarative method names  03 Feb 2018
Using Premeditation  27 Jan 2018
With Relocation  20 Jan 2018
Using Consolidation  13 Jan 2018
Using Semantic Types  06 Jan 2018
Using Extension Methods  30 Dec 2017
Error Methods  25 Nov 2017
More smart-code posts »
January 2010