Thursday, September 04, 2008

Came upon this article on mapping custom collections that uses extension methods. I'm not a massive fan of extension methods to be honest and I prefer to use custom collection classes and I thought I'd show how I've seen them mapped in the past.

When I say that I like custom collections I mean classes like this:

public class FooCollection : ReadOnlyCollection<Foo>

I prefer a collection like this over a bog standard IList<Foo> because there is usually a lot of behaviour related to the collection, for example we may have custom rules relating to addition/removal and we'll probably want to ensure the collections contents are valid at all times. One solution would be to use encapsulate collection but I've found that it just results in my aggregate roots and key entities getting bogged down in lots of boring collection work so I prefer to farm out this work to custom collections. Plus if I go for the custom collection approach I can ensure they implement interfaces such as these:

This sort of interface is very useful because IEnumerable misses some important members and IList is too permissive of change, plus we want to be able to add all sorts of useful helper methods like the FindAll shown.

However I can't directly map my FooCollection, NHibernate is only going to be able to map something basic like an IList. The solution is to map the collection like this:

This requires a little explaining. We're mapping our custom collection as a component, this seems odd until you look at the way we then map the _innertList within our custom collection. Essentially this _innerList is an IList<Foo> hidden within FooCollection (or more correctly one of its base classes). Since we're mapping an interface NHibernate is happy and the users of our custom collection don't know that we've had to put in this silly _innerList, plus maintaining/using this little internal collection is taken care of by a base class so its all painless. One slight smell is that we're mapping _innerList as a field, not perfect but its not going to keep me up at nights.

Anyway its not a perfect solution but it lets you map custom collections without sacrificing too much.

10 comments:

We came up with a solution like this and it is a useful shortcut, if you don't want to go with a full blown custom NH collection implementation.

One niggle is that you need to include the name of the inner list field in the property path within your Criteria queries, e.g. .AddCriteria("OrderLines._innerList.Product.Id"... Naming the inner list something like "Items" minimises the mess.

The specifications in the approach I'm describing were just for in-memory as this was pre-Linq but when I next try it I'm going to try Linq specifications and then you could use them in memory and/or could use them in the repositories.

Only problem I have with that approach right now is that Linq to NHib isn't fully enough implemented to support some of the things I need.

@DaveSorry yeah, basically your FooCollection inherits either directly from IReadonlyCollection or from ReadOnlyCollection (a base class that takes care of 90+% of the nonsense needed to implement IEnumerable and so on).

Means I can expose the FooCollection directly, or expose IEnumerable or expose IReadonlyCollection.

In FooCollection you add any custom behavior e.g. disallowing adding Foos if there is another Foo of that type already in the collection.

Excellent post as always, but it leaves me a little confused about the role of custom collections vs repositories. Repositories emulate a custom collection, thereby abstracting persistence concerns. Assuming Order is an aggregate root, are you saying you might have an OrderRepository and an OrderCollection?