Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Custom Collections with NHibernate, Part IV: Extensions!

In part I of this series, we examined motivations for maintaining custom collections that are compliant with NHibernate.  Part II demonstrated the basics behind wiring up a custom collection.  Finally, part III refactored the solution into a nice reusable package with minimal intrusion (relatively speaking).  My complaint with all of this is that the proposed solution is complicated, difficult to explain, and leans towards Shotgun Surgery by requiring you to change the signature of both the POCO collection as well as the NHibernate collection every time you need to add a new method.  That's just not cool.  As much bad press that extension methods have received, I have found one instance (and only one so far) when extension methods truly shine:  extending collection interfaces, which NHibernate can happily bind to, with custom methods.

Ideally, we want our custom collection to be completely ignorant that it's being bound to by an ORM.  In the previous suggestion (part III), we ended up with an IProducts interface to define the custom collection, a Products class to implement the custom methods, and a PersistentProduct class for NHibernate to bind to.  (We also had a very ugly HBM mapping, but I digress.)  This all seems a bit overkill and more than a stone's throw from being completely ignorant of the data-access mechanism.  For this demonstration, assume that a Customer has one or more orders; furthermore, we'd like to be able to add custom query methods onto the orders collection.  But since NHibernate has to bind to an interface, let's start there within our Customer class:

/// <summary> /// Assuming we want to leverage automatic properties, /// init the collection here. /// </summary> public Customer() { Orders = new List<Order>(); } /// <summary> /// Note the protected set; only the ORM should set the collection /// reference directly after it's been initialized in the constructor. /// </summary> public IList<Order> Orders { get; protected set; }

Now that we're just binding to a collection interface, the mapping becomes trivially simple:

<bag name="Orders" inverse="true"> <key column="CustomerID"> <one-to-many class="Order"> </bag>

Are you starting to get a sense for how much simpler this is going to be?  So we've got NHibernate binding to the collection, but we still need to provide custom methods on the collection; that's where extension methods come in!  Suppose that you have a need to regularly query the collection for all the orders that fall on a particular date.  As we concluded in part I of this series, we'd ideally like this mechanism to be on the collection itself.  By adding an extension method to the IList<Order> interface, our task is complete:

public static class OrdersExtensions { /// <summary> /// Extends IList<Order> with other, /// customer-specific collection methods. /// </summary> public static List<Order> FindOrdersPlacedOn(this IList<Order> orders, DateTime whenPlaced) { return ( from order in orders where order.OrderDate == whenPlaced orderby order.OrderDate select order) .ToList<Order>(); } }

With the extension method in place, we now have full use of the method from an IList<Order> collection with intellisense.

Custom Collection Extension Intellisense 

So where's the rub?  The drawback to this is that extension methods are a bit evil magical and can be difficult to notice if they're not kept well organized.  Accordingly, follow these guidelines when using this approach to make it easier for the next developer (usually yourself) who looks at your code:

  • Name the file (and class) which contains the extensions as <domain object plural>Extensions.cs; e.g., OrdersExtensions.cs, CustomersExtensions.cs.  This is similar to what we were doing before, e.g. Products, but tacks on the word "Extensions" to make it clear that these are extensions and not an actual collection object.
  • Put the extensions class physically next to the domain object which it works with.  For example, if the Order.cs class file sits in the directory MyProject.Core/Checkout, then OrdersExtensions should sit right next to it.  This makes it easier to spot when browsing the project files.
  • Keep each collection of extension methods logically contained in individual files, one for each domain object they work with.  Doing so emulates what we would have done if we were using "traditional" custom collection classes where we would have had e.g. Orders.cs and Products.cs.  Contrastly, you shouldn't put every extension method into a ginormous MyCollectionExtensions.cs class.  This breaks the SRP, makes it difficult to digest when it gets larger, and can quickly become a maintenance headache nightmare.

I've been using this approach for the creation of custom collection behavior for the past six months or so and have been very pleased with the ease of use and extensibility.  They're also leveraged in the S#arp Architecture example code.  While extension methods may be a bit on the evil side, they make the challenge of using custom collections with NHibernate terribly trivial.  >-)

Billy McCafferty


Posted 09-03-2008 4:10 PM by Billy McCafferty
Filed under:

[Advertisement]

Comments

Reflective Perspective - Chris Alcock » The Morning Brew #172 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #172
on 09-04-2008 3:20 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #172

Colin Jack wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 8:22 AM

Why not just have a custom OrderCollection?

I maybe should have read the other posts first but thats the approach I've used and its cleaner than extension methods and easy to map (just map your OrderCollection it as a component with the wrapped IList being whats actually wrapped as a bag).  The tiny tiny little bit of dirtiness is then hidden in the mapping file.

This solution has other advantages, for example it allows me to add interfaces to the collections e.g. IReadOnlyCollection (because IList/IEnumerable bother have annoyances).

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 8:30 AM

Colin, could you share an example HBM mapping of that?  Would your OrderCollection then inherit from List<Order>, for example?

Dew Drop - September 4, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - September 4, 2008 | Alvin Ashcraft's Morning Dew
on 09-04-2008 12:28 PM

Pingback from  Dew Drop - September 4, 2008 | Alvin Ashcraft's Morning Dew

pete w wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 1:47 PM

Very nice, this makes things much simpler.

In my short experience with the .NET 3.5, I've found that extension methods are handy for cases when I need to tweak to code that "isnt mine".

-protected internals

-intricate/delicate libraries

This is a great use of extensions

Arjan`s World » LINKBLOG for September 4, 2008 wrote Arjan`s World &raquo; LINKBLOG for September 4, 2008
on 09-04-2008 2:54 PM

Pingback from  Arjan`s World    &raquo; LINKBLOG for September 4, 2008

Jak Charlton wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 3:38 PM

Nope ... still hate exntension methods ... and I really don't think this is at all a good way to use them ... Colin's approach is closest to the one I use too ...

As I'm busy using ASP.NET MVC at the moment which has  large bias towards them, I am pretty sure they should be forever banished to the deepest darkest depths of hell!  :)

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 4:02 PM

First of all, tell me how you really feel about extension methods. ;)

Secondly, could you elaborate more on your approach?

Thanks!

Colin Jack wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 5:04 PM

@Billy

There must be info about it somewhere but just decided to write my own blog entry about it instead:

colinjack.blogspot.com/.../nhibernate-mapping-custom-collections.html

There may be better ways of doing it though, this approach is quite old.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-04-2008 5:30 PM

Thanks Colin!  As you implied, I recall seeing something like that a couple years back.  My only complaint, as you pointed out, is binding to _innerList.  I guess it comes down to a bit of "pick your poison"; complicated abstraction, adding extension methods or burrowing through encapsulation.  ;)

IHateSpaghetti {code} wrote Blog Carnival #4
on 09-05-2008 3:50 PM

DSL Tools DSL Tools SP1 features - Nested shapes and improvements - by Jean-Marc Web Firefox, ClickOnce

Steve wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-06-2008 7:11 AM

Some clarification for the slow ones here:

Is this custom collection providing a nhibernate query to the database, or is it just an 'in-memory' query of the collection ?

ie. Billy - is that linq or NHibernate.Linq ?

I want the flexibility to make that query - when using lazy load for example - but I'm not sure how to handle the NHibernate session within the static extension class?

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-06-2008 9:18 AM

To answer your first question, it's both.  It's the collection that NHibernate will watch for changes to and it's the in-memory collection which you can perform further query operations on.

The example uses LINQ.

As for how to use the session properly, you can take a look at code.google.com/.../sharp-architecture for an example.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-06-2008 9:22 AM

Ah, you're talking about using the static extension class from the custom collection.  I wouldn't recommend invoking the session object from the custom collection, since it resides in the domain.  If you needed to access the session from the collection, I'd recommend adding a collection interface to the domain layer, the implementation in the data layer, and then you could write a small custom service locator to return the current session.  You'd likely use part III of this series to write the collection itself.

But accessing the session from the collection itself isn't an approach I'd recommend; I'd instead recommend the use of a DAO to encapsulate the data access methods which the custom collection could then use.

Steve wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-07-2008 8:44 AM

Thanks.  It does pose an interesting problem.  Take your example where I want to goto the database to query for orders by a date - which I would think would be better than looping through orders in a lazyload situation ?  (Isn't this going to make multiple select calls for each item, or do a select all of orders ???)

But, the domain call to get 'GetOrdersByDate' is something I expect on my Orders domain object, but, as in my code, it's in the dao - however, I find it ends up putting this 'business logic' in the data access layer.

I don't keep a session in my domain objects - I use the Sharp architecture - at the same time, it's my least liked part of the separation.

Basically - if the rule requires a data call, it becomes a dao call, if it's 'in memory' on the collection, then it is in the domain ?

Great series of articles.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-08-2008 1:10 PM

Hi Steve,

A nice side effect of S#arp Architecture is that domain objects can use DAOs as well.  They're aware of IDao and any other DAO interface that you've placed within the MyProject.Core/DataInterfaces folder.  Consequently, you can pass an Order DAO to the Orders collection if you'd like all similar calls to go through the collection.  In the case you've described, the Orders collection would simply hand off responsibility to the DAO that it was handed.  In this way, you can loop through the items if that's the best way to do it, while leveraging DAO methods, via the Orders collection, if the situation requires it.

Turkey wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 09-10-2008 1:09 PM

DSL Tools DSL Tools SP1 features - Nested shapes and improvements - by Jean-Marc Web Firefox, ClickOnce

Flannery Culp wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 10-30-2008 9:01 AM

If I add an Order to the Orders of a Customer, how do I get NH to automatically add the order into the database?

Should I use extensions as above to extend the functionality of the Add method?

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 11-14-2008 12:24 AM

Flannery,

There are a couple of ways to deal with this.  You can set inverse=false on the parent of the one-to-many relationship and have the cascade take care of saving the new item.  Otherwise, you can have the parent maintain inverse=true, which is a better practive IMO, and call SaveOrUpdate on the new child.  Having the extension method shouldn't have any impact on this behavior.

David Veeneman wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 12-04-2008 1:30 PM

First, thanks for the series--I am finding it very helpful.

A quick question: Will the extension method approach work for implementing interfaces? I am assuming that it will not. So, to implement INotifyCollectionChanged, for example, I will need to implement the approach you describe in Parts 2 and 3. Is that correct?

Thanks again.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 12-10-2008 5:34 PM

I believe that's accurate David but have not tried to implement INotifyCollectionChanged with extension methods.

Jeri wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 04-06-2009 6:18 AM

Best site in world.

I am from Finland and now teach English, give please true I wrote the following sentence: "A few weeks after it enticed many new customers with the exclusive google t mobile is dangling another incentive to get more."

With love :-(, Jeri.

Gold wrote re: Custom Collections with NHibernate, Part IV: Extensions!
on 02-01-2010 6:58 AM

thanks for the series.I am finding it very helpful.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)