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
Using Constructors  27 Feb 2023
An Inconvenient API  18 Feb 2023
Method Archetypes  11 Sep 2022
A bash puzzle, solved  02 Jul 2022
A bash puzzle  25 Jun 2022
Improve your troubleshooting by aggregating errors  11 Jun 2022
Improve your troubleshooting by wrapping errors  28 May 2022
Keep your promises  14 May 2022
When are you done?  18 Apr 2022
Fixing GitHub Authentication  28 Nov 2021
January 2010