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 II: The Basics

Download the sample code for this post.

In part one of this three part series, I offered what we’d ideally like when using NHibernate, or any ORM for that matter, with custom collections in the domain layer.  To reiterate, the ORM should have as little impact as possible on how custom collections within the domain are structured and how relationships between classes are declared.  At the very least, there should be no references to the ORM, or ORM-supporting assemblies, such as the deprecated NHibernate.Generics, from the domain, whatsoever.

These goals are a bit vague, so let’s make them a bit more measurable:

  • We should be able to create a custom collection which does not depend on data layer references.
  • The custom collection’s container should be able to hold a pointer to the custom collection without having to go through a wrapper class or data layer intermediary.
  • The entire domain layer should contain no assembly references to data layer dependencies.
  • And, obviously, NHibernate’s behavior should not be adversely affected by these restrictions.

Well, that sounds simple enough.  The example solution, using NHibernate 1.2.1, shows the bare minimums for achieving these goals.  To get the sample working locally, simply modify the connection string within /CustomCollectionsBasic.Web/bin/web.config to point to your Northwind database.  (Keep in mind that this is an illustrative sample so there'll be plenty to nitpick in the presentation layer.)

The CustomCollectionsBasic.Core Class Library

The first three goals concerning keeping the domain layer clean are a good place to start, so let's take a look at the domain layer which is in the CustomCollectionsBasic.Core class library.  This class library maps two classes, Category.cs and Product.cs, to their respective tables in Northwind and has a one-to-many relationship from a category to zero or more products.  Our motivating factor for wanting to create a custom collection, in this example anyway, is to be able to sort the products associated with a category.  (There are better reasons to have a custom collection...but this'll do until I find some creativity.)

Let's take a closer look at Category.cs.  You'll note that it inherits from DomainObject which enables domain objects to be compared conveniently and provides objects with their ID property to be used for persistence.  Obviously, this exists due to data layer requirements, but it's never given me grief like collection management has.  Note also that this inheritance doesn't have anything to do with custom collections; it doesn't implement anything from a data layer to enable custom collections.  The virtual keywords exist to allow lazy loading of the object; these are optional and could be removed altogether if you're OK with the object not being lazily loaded.  But again, having the virtual keywords don't muddle the waters much and aren't going to create a likely area for the introduction of bugs.  GetHashCode is an implementation detail of DomainObject; you can read about the DomainObject class and all its gory details in a previous post.

Now, the interesting part (or I suppose uninteresting is our point here) is the collection of products that the category class holds.  The ProductsInCategory property has a POCO getter and setter, exposing an IProducts interface.  Working via an interface for the collection is the only effect of having to accommodate the ORM, and it's very unobtrusive as the interface simply implements IList<Product>.  Furthermore, you'll note that the collection's member field, productsInCategory, has been initialized as a new POCO products collection and it's setter is protected so you never have to worry about it being null when dealing with someCategory.ProductsInCategory.

The following diagram illustrates the gist of the domain model:

As the diagram shows, the Category.cs class points to a collection, the IProducts interface.  The Products.cs collection simply implements that interface.  And that's it.  The domain is very clean with respect to managing the custom collection...no wrapper class and no ComplicatedPersistentListBaseClassComplicatingItAll.

So that knocks off the first two goals concerning the creation of a clean, custom collection and the relationship to it from a container class.  And to verify the the third goal, concerning having no data-layer, assembly references in the domain, simply take a look under CustomCollectionsBasic.Core/References.  Besides the HBMs, the only other thing in this class library is the Design-by-Contract utility written by Kevin McFarlane.

Obviously, the HBMs are data-centric and break up the data-concern-aversion-ness (is that a word?) of the domain layer.  But having them in the domain is strictly for convenience and ease of maintenance.  They could just as easily be put into the CustomCollectionsBasic.Data class library...which brings us to our next area of discussion...  (We'll come back to the HBMs in just a moment.)

The CustomCollectionsBasic.Data Class Library

This library holds all the ugly stuff for managing the NHibernate session (/NHibernate/NHibernateSessionManager.cs), setting up the generic DAO (/NHibernate/NHibernateDao.cs) and concrete DAO for the Category class (/Daos/CatgoryDao.cs), and, finally, and most importantly, holds the custom collection implementation details that NHibernate needs in order to work through the IProducts interface in the domain layer.  (For a discussion of the NHibernateSessionManager class, you can read my NHibernate article on codeproject.com.)

In order for you to create custom collections that NHibernate can work with, you need two classes:

  • A Persistent Collection (/Collections/PersistentProducts.cs):  The persistent collection is the custom collection in "NHibernate speak" and is what NHibernate is capable of working with.  The simplest way to create your own is to simply inherit from NHibernate's PersistentGenericBag or a similar such collection.  Looking at the code, you'll note that the PersistentProducts.cs class also implements the IProducts interface that we defined in the domain layer.  This is an example of a Separated Interface (or Dependency Inversion) and provides a means for creating a clean separation of concerns between the domain and the data layer.
  • A Persistent Collection Type (/Collections/PersistentProductsType.cs):  This is the type declaration of the collection so that NHibernate can interpret an HBM mapping and translate the results from the database into the custom collection, accordingly.  The type implements NHibernate's not-very-fun-to-implement IUserCollectionType interface.

Now, to make this all work, a mapping has to be provided in Category.hbm.xml, back in the domain layer, that instructs NHibernate how to wire it all up.  The example code reflects:

<bag name="ProductsInCategory" table="Products" cascade="all" inverse="true" collection-type="CustomCollectionsBasic.Data.Collections.PersistentProductsType, CustomCollectionsBasic.Data"> <key column="CategoryID"> <one-to-many class="CustomCollectionsBasic.Core.Product, CustomCollectionsBasic.Core"> </bag>

That's it for the data layer and wiring it all together.  Running the Default.aspx page will show it all in action with lazy loading and all.

In the other NHibernate custom collection samples I've found, there's a lot more code than what I have in mine.  I'd love for anyone to let me know if I've missed something here; but I don't think I have.  This solution supports lazy loading and I've been using it in a variety of parent/child scenarios without any trouble.

So Who Needs a Part III?

This sample does a good job of meeting our goals by keeping the domain layer clean of fragile collection wrappers and/or data-layer references, but the magic that makes it happen isn't very reusable.  Take a look at /Collections/PersistentProductsType.cs.  Because of its direct dependence on /Collections/PersistentProducts.cs, you'll have to create an almost identical type class for each and every collection that comes along.  Good luck changing them all if you find a bug or have a change to make for some reason.  In the next part, I'll show how we can make the type class generic without having to create a complicated class hierarchy.  The downside is that our HBM collection mapping is going to get a bit uglier...er...more fun.  What, you thought nothing would have to give? ;)  We'll also see a few of those data-oriented classes pulled out into a reusable library.

Until then, happy enumerating.

Billy McCafferty


Posted 12-06-2007 9:58 PM by Billy McCafferty
Filed under:

[Advertisement]

Comments

Luis Abreu wrote re: Custom Collections with NHibernate, Part II: IList<Basic>
on 12-07-2007 6:30 AM

Hello Billy,

great  post again.

I do have one question (which might be relaetd with my lack of experience with nhibernate). How do you adapt your objects to services? ok, you can build new wrappers but I'm interested in using the same objects i have in my model.

what i do is use an interface that is responsible for getting the data and this interface gets implemented by a cusomt class that knows which service to call to get a collection of elements (and which is being passed to the the object when it's used on the client - ex.: when the object is instantiated ina win forms app) and by a repository object (which is used on  the server side).

the main problem i have is that persisting the data with this approach sucks and i do have to write more code with it...

so, do you have any advice for this kind of scenario?

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 12-07-2007 9:24 AM

Hi Luis, I'm not sure I understand the gist of the question.  What specifically "sucks" with respect to your current approach to persisting the data?

DotNetKicks.com wrote Custom Collections with NHibernate, Part II: The Basics
on 12-07-2007 9:36 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Billy McCafferty wrote Custom Collections with NHibernate, Part III: Refactored
on 12-17-2007 2:16 PM

[ Update on Dec. 17, 2007: Removed extraneous CustomCollectionsPro.Core.CollectionInterfaces namespace

Billy McCafferty wrote Custom Collections with NHibernate, Part I: Who Cares?
on 12-17-2007 2:18 PM

When people speak of the object-relational impedance mismatch , they usually talk about the differences

Luis Abreu wrote re: Custom Collections with NHibernate, Part II: The Basics
on 01-03-2008 9:30 AM

Hello again Billy.

Let me start by wishing you a Happy New Year.

Now, back to the question. I've already read the 3rd part of the article and I do like the way things work. Now, to me the problem is: how do you expose your model using, for instance, WCF and reuse the same objects on the client and server side?

what i've done is create an interface that defines the operations responsible for loading the  items of the collection and then i have a client side class that implements that interface by calling a wcf service and, on the server side, have another class that performs the same thing by going to the db (in fact, the service reuses this later class to perform that kind of work).

Now, this works, but it's not really code that I enjoy writing. what do you do in these scenarios? That's is something I'd like to get more info: how to use nhibernate on the server side and expose your domain model by using WCF services...

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 01-08-2008 11:52 AM

That's a good question Luis.  I do not have experience with WCF and would recommend that you present your question on the NHibernate forums at forum.hibernate.org/viewforum.php.  If you have luck in finding an answer, I would be very interested in hearing about it.

Thank you.

Symon Rottem wrote re: Custom Collections with NHibernate, Part II: The Basics
on 03-31-2008 10:17 AM

I've been wondering whether this approach could be used for custom lists that have a custom constructor that takes the collection's parent in the constructor so I can create collections that wire up the other end of a bidirectional association, but I can't see how this would be accomplished.

Do you have any idea of whether or not this is possible?

Billy McCafferty wrote Custom Collections with NHibernate, Part IV: Extensions!
on 09-03-2008 7:16 PM

In part I of this series, we examined motivations for maintaining custom collections that are compliant

Community Blogs wrote Custom Collections with NHibernate, Part IV: Extensions!
on 09-03-2008 7:53 PM

In part I of this series, we examined motivations for maintaining custom collections that are compliant

Josh Berke wrote re: Custom Collections with NHibernate, Part II: The Basics
on 09-08-2008 2:09 PM

Hello nice series so far. I have a question, in your PersistentProducts implementation your SortBy method returns a new ProductsList where as in Products it returns the same list.

If the base constructor on List maintains a refernce to the list then I think this is ok since your persistent collection would be re-ordered.  If it doesn't (Which without digging out reflector is how I think it would behave), then now you have what the client thinks is a single object with two different behaviors depending on if the collection came from nhibernate or was just newed up.

My concern with this approach is the possiblity of duplicating code, in order to maintain the clean seperation.

Maybee I've just been reading code to long today and my mind is scrambled.

-josh

Thierry Nenin wrote re: Custom Collections with NHibernate, Part II: The Basics
on 10-15-2008 2:25 PM

Luis Abreu asked on January 3, 2008 about WCF,

About interfaces, The MSDN Docs states that

Interface types are treated either as Object or, in the case of collection interfaces, as collection types...

So far I've played with WCF, I never try to serialze Interfaces but it seems posisble to seralize IList, IList<T>, IDictionary, IDictionary<T>... Do you need antything other? I think no.

What i am also sure is that you can put all "DataContract" (I.e. your buisiness objects that hav eto be serialized/deserialized by WCF) in a reusable dll that you can distirbute on the client. It is just that you need to correctly inform the Wizard that generate the client proxy about those "well known types"... Even with the svcutil cmdline, it is possible to include types in your dll as "well known type". I've already done it.

I hope this will help.

Thierry.

Mike wrote re: Custom Collections with NHibernate, Part II: The Basics
on 11-07-2008 12:46 PM

Outstanding stuff. Very useful. Here's a question though. I have inherited a code-base that has its own persistence framework (home brewed). The framework is absolutely terrible and I am trying to ween the developers away from using it. The problem is all of the business objects use a custom oDataList object instead of the standard List or IList. oDataList does not even extend any of the standard collection classes, and it uses a Microsoft.VisualBasic.Collection internally to store items.

The biggest problem is, Microsoft.VisualBasic.Collection is basically a Hash Map. Every item is keyed. This makes the IUserCollectionType methods like IndexOf impossible to implement since there is no index. Is there an alternative to IUserCollectionType that I can use instead? Perhaps a Map (hash map) type instead?

Thanks for your help.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 11-14-2008 12:14 AM

I am unaware of an alternative to IUserCollectionType to be used instead.  If you're on C#, you could use extension methods instead to add capabilities to the collections.

David Veeneman wrote re: Custom Collections with NHibernate, Part II: The Basics
on 12-04-2008 10:20 PM

Thanks again for the great series!

I'm puzzled about something: In the demo, we have three different classes representing the products collection: Products, PersistentProducts, and PersistentProductsType. The first two classes implement IProducts, but the third doesn't. And its the third type that we specify as the collection-type in the mapping file. Huh?

I'm lost on what's going on and how NHibernate is using these three classes. Well, to be more precise, my head is swimming. Is there an article or blog post that explains what's going on here? If not, could you provide a brief walkthrough of what NHibernate is doing with the three classes. I can see how to set them up--I'm awfully puzzled as to why. In other words, I am struggling with how to put the pieces of this puzzle together.

Thanks for your help, and for all the great articles you have written. They are helping my get my arms around this subject.

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 12-05-2008 5:21 PM

Hi David,

The best way to see the details and get a hardcore understanding is to tear open the NHibernate source code.  I have to admit that I cannot detail all of the internal workings but understand what needs to be done to make it work. ;)  Alternatively, the NHibernate team is very active on the NHibernate forums and may be able to assist better with explaining some of the low level nuances of what makes this solution work.

David Veeneman wrote re: Custom Collections with NHibernate, Part II: The Basics
on 12-09-2008 9:02 AM

I found this post on the NHibernate forums that explains most of the workings:

forum.hibernate.org/viewtopic.php

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 12-10-2008 8:21 PM

Great find David...thanks for sharing!!

zoli wrote re: Custom Collections with NHibernate, Part II: The Basics
on 09-25-2009 12:16 AM

Can't download the sources, can you put it back?

Billy McCafferty wrote re: Custom Collections with NHibernate, Part II: The Basics
on 10-14-2009 1:13 PM

All set zoli...thanks for letting me know!

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)