Devlico.Us
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @devlicious

Alan Northam



Getting Started with NHibernate: Part III

    When mapping objects to the database in NHibernate you can either use XML files or decorate your classes with mapping attributes (NHibernate.Mapping.Attributes).  I've used both but I usually choose XML files simply as a matter of personal preference.  Either method works well and both provide many options for defining the relationship between class and table and configuring it's function.  I'll be using XML files in this tutorial but may work in some mapping attributes later in the process. 

    To get intellisense working with the mapping files you can copy the nhibernate-*.xsd files from the NHibernate source (NHibernate\src\NHibernate\) to C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas (or wherever you installed VS).  There are a few tools that can help by generating the mapping files based on an existing database schema, one that I've had the most success with is MyGeneration (try the template by lujan99).

    As I go through each mapping I'll point out how certain features work but for detailed information on the available options check the official documentation here.  Each of these mapping files will follow the naming convention classname.hbm.xml as this is what NHibernate expects to find when the SessionFactory is initialized (more on this in Part IV).

 

    BlogPost mapping: 

    At the beginning of the file I'm setting the assembly and namespace values.  They aren't required, but if they are not set you will need to fully qualify any class names used including the assembly ( i.e., NHBlog.Domain.BlogPost, NHBlog.Domain).  The class element tells NHibernate what the name of the class is and which table that class maps to, in this case both are named BlogPost.  The id element defines the primary key of the table and which property in the class it maps to.  It's also includes the type (.net data type) and which class is generating these values.  NHibernate includes many different generator classes.  In our example we are using the identity function in SQL Server so the generator is set to native. 

    Each BlogPost will also have a collection of Comments seen here mapped using the bag element.  This mapping is a bit different than normal in that the<loader query-ref="GetCommentsByBlogPostID"/> attribute specifies which query to use to load the collection of comments, in this case a stored procedure.  At the bottom of the file a sql-query is defined to execute the stored procedure GetCommentsByBlogPostID and pass in the value blogPostID.  The load-collection element instructs NHibernate what type of entities to expect as the result of this query.

    Why am I using a stored procedure here?  I don't have to, I could load the collection like any other but doing it this way serves two purposes; to demonstrate the use of stored procedures (a new feature in NHibernate 1.2), and to assist in loading the comments in a threaded conversational manner (see the requirements in Part II).  GetCommentsByBlogPost uses a feature in Sql Server 2005 (and Sql Express 2005) called Common Table Expressions.  More information can be found here.

 

    BlogUser mapping: 

    In BlogUser we have three collections mapped; BlogPosts, Comments, and BlogRoles.  The first two are one-to-many relationships meaning that a single BlogUser can have posted many BlogPosts and made many Comments.  The last is a many-to-many relationship meaning that a BlogUser can belong to many BlogRoles and a BlogRole can contain many BlogUsers.  The table UserRole defines this relationship in the database.  The mapping instructs NHibernate what type of class makes up this collection, which table to use to look up the collection members and which column holds the key for each.

 

    BlogRole mapping: 

     BlogRole has a collection of BlogUsers which represents the other side of the many-to-many relationship defined in BlogUser. 

 

   Comment mapping:

    A Comment has a reference to the BlogUser who made it (MadeBy), the Comment it is replying to (InReplyTo), if any, the BlogPost it is a comment on (BlogPost), and a collection of comments replying to it (Replies).  Also, notice the "Depth" property doesn't allow inserting or updating of this value.  That's because this value/column doesn't actually exist in the database, it's generated when loading a collection of comments.  If you load an individual Comment this value will always be zero.  This mapping is also using a stored procedure but in this case it will be used when loading a comment by id. 

 

    GetCommentsByBlogPostID stored procedure code: 

   

    GetCommentByID stored procedure code: 

   

    In Part IV we will configure NHibernate and begin building the supporting framework needed to use what we have done so far.



Comments

Derik Whittaker said:

Alan,

As a complete newbe to NHibernate I have to ask 2 things.  

1) What do I name my xml config files?  Does it even matter

2) Where do you suggest i put them in my solution?  Does it even matter

Derik

# May 24, 2007 7:40 AM

anortham said:

Derik,

In the next article I'll cover this in a more detail, it will be all about NHibernate configuration, setting up Visual Studio, and getting a unit test project running.  

The quick answer is name the files classname.hbm.xml, put them in the assembly with your domain class files and set the build action to embedded resource.

# May 24, 2007 9:35 AM

Billy McCafferty said:

Taking this a bit further, put your HBM file in the exact same folder of the object it describes.  So if you have Customer.cs in a folder called Organization in your project, then Customer.hbm.xml should be in that same folder, right next to Customer.cs.  This makes it very easy to switch between the HBMs and the domain objects they describe.

# May 24, 2007 11:33 AM

jhunter said:

When I use bag to try and map to a collection I get an exception that says:

Unable to cast object of type 'NHibernate.Collection.PersistentBag' to type 'ClassLibrary1.EmailAddressCollection'

EmailAddressCollection is just a class that inherites List<EmailAddress> is there some other way I have to implement a collection in the object?

# May 24, 2007 12:08 PM

anortham said:

jhunter,

In the BlogPost class above, the collection of comments in the mapping is implemented in BlogPost using .net 2 generics as:

public virtual IList<Comment> Comments {get;set;}

# May 24, 2007 12:38 PM

anortham said:

I've attached a zip file containing the domain project I was using for the article.

# May 24, 2007 12:54 PM

PartialClass said:

Good work Alan.

At last, i have started to learn some NHibernate :)

I am looking forward to next "episodes".

I am also thinking of re-compiling these tutorials in more "user (developer) friendly" way.

I am sure there will not be any copy rights to these tutorials :)

# May 24, 2007 11:02 PM

anortham said:

PartialClass,

No copyrights :)

# May 25, 2007 8:54 AM

anortham said:

Billy, Derik,

I updated the article to clarify the naming convention for the mapping files.

# May 25, 2007 11:41 AM

Michel Kommers said:

Very nice sample application, but have an error when using membership profile with encrypted password

# July 10, 2008 5:06 PM

Chris May said:

# September 16, 2008 3:17 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!

Our Sponsors

Proudly Partnered With