Spending time with Fluent NHibernate: Part 1




A couple of days ago, there was a post by Howard Dierking covering the topic of Fluent NHibernate. What caught my attention was one of the comments:

I find this a little weird... isn't it so that in general, nhibernate supporters loath the 'domain model following the database tables' approach, while to get automapping, you practically have to? ;)

About the PI issue: there's no such thing as persistance ignorance: whatever persistence logic you're using, it bleeds into your own code one way or the other, be it a base class, be it requirements how to define properties/members, be it how to deal with 2-sided relationships etc.

 

I want to focus the following series of posts on showing what Fluent-NHibernate (NHibernate) is truly about, and let you be the judge of whether the previous comment is correct or not. My stand on it is that it's not.

So without further ado, let's start by the basics.

[Note: Due to time limitations and also to keep posts short, I will try and focus on small sections on each post]

 

NHibernate 101 in one paragraph

For those new to NHibernate, it is an ORM framework (Object Relation Mapper). ORM's try and eliminate the impedance mismatch that exists between Object Orientated programming and Relational Databases. This allows developers to work with objects and not have to worry about how these objects are persisted to disk in a relational database (i.e. tables, joins, etc.). Other examples of ORM's include LLibGenPro, Linq to Sql, and of course Entity Framework (although some people claim that EF is MORE than a ORM, but each to their own).

 

Mapping in NHibernate

One of the main problems with any ORM is that there is considerable amount of time spent mapping objects to their corresponding tables in a database. Imagine the following class:

   1: public class Customer 
   2: {
   3:     public virtual int Id { get; private set; }
   4:     public virtual string NameFirst { get; set; }
   5:     public virtual string NameLatst { get; set; }
   6:     public virtual string Telephone { get; set; }
   7:     public virtual string Email { get; set; }
   8: }


This needs to be stored in the database somehow, and to do so, the framework (in our case NHibernate), needs to know how to match objects to tables and properties to columns. To solve this, NHibernate uses the concept of mapping files (hbm), which are non other than XML files with specific tags:


1:
<?xml version="1.0" encoding="utf-8" ?>
   2:  <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"  
   3:    namespace="Examples.Domain" assembly="Examples.Domain">  
   4:  
   5:   <class name="Customer" table="Customer">  
   6:      <id name="Id">  
   7:       <generator class="identity" />  
   8:      </id>  
   9:      <property name="NameFirst"/>  
  10:      <property name="NameLast"/>  
  11:      <property name="Telephone"/>  
  12:     <property name="Email"/>  
  13:  </class>  
  14:</hibernate-mapping>  


The configuration is pretty self-explanatory. You define the name of the class, the corresponding table name you want in the database, and then start to define a series of properties. NHibernate follows a series of conventions, so for example if a column attribute is not specified in the property element, the corresponding column name in the database table will be the same as that of the property. The same thing happens with the type of the property, again, NHibernate internally maps CLR types to database types.


One of the problems with this type of mapping is that you don't get type-safety. If you make a mistake in writing out a column name you won't know of the issue until runtime. The other problem occurs when you are trying to refactor your code. If you don't have special add-ons such as Resharper (and if you don't, what are you waiting for?), refactoring XML files is pretty much a no-no in Visual Studio. Imagine the scenario: you rename NameFirst to FirstName and forget to do it in your mapping file. Your application will still compile, but come runtime, kaboom!


Fluent-NHibernate 

This is where Fluent-NHibernate enters the picture. This project was initially built to allow for developers to map their entities to tables in code, i.e. using C#.

   1: 1. public class Customer : ClassMap<Customer>  
   2: 2. {  
   3: 3.   public Customer()  
   4: 4.   {  
   5: 5.     Id(x => x.Id);  
   6: 9.     Map(x => x.NameFirst);  
   7: 9.     Map(x => x.NameLast);  
   8: 9.     Map(x => x.Telephone);  
   9: 9.     Map(x => x.Email);  
  10: 2.   }  
  11: 3. }  


Already this provides benefits. By allowing this, you get away from the magic strings in the mapping files, allowing not only for compile-time checking but also refactoring.

However, one thing still remains, mapping. You still have to map your classes. Now, during both the mapping using XML as well as C#, have you noticed one thing? A series of assumptions are made. We said previously that you don't need to specific column names or types, etc. These are conventions. NHibernate assumes a series of conventions. Following the 80/20 rule, assuming that 80% of the time these conventions suit our applications, there's a remaining 20% that would  need some sort of adjustment.



Auto Persistence Model

Say Hello to the Auto Persistence Model in Fluent-NHibernate. By latching on to the concept of convention over configuration, the Auto Persistence facilities in Fluent-Nhibernate do the mapping for you. This makes a series of decisions and maps your classes to their corresponding database tables and columns. Yet at the same time it allows you to tweak these conventions. This gives us 100% coverage. For 80% of the time, the APM is fine, and for those special cases we adjust the conventions to our needs. Here is the same mapping using the Auto Persistence Model in Fluent-NHibernate :


   1: AutoPersistenceModel.MapEntitiesFromAssemblyOf<Customer>().Configure(config);

AutoPersistenceModel has a static method MapEntitiesFromAssemblyOf where you pass in a specific type. Since my current example only has one type, namely Customer, I'm passing this one in. It takes this assembly and loads all the types it contains and create the NHibernate mappings for you. It places these mappings into a NHibernate Configuration class (the config parameter).    

So far, the code for the simple example project is limited to the Customer class and the single isolated line of code that generates the mapping using Fluent-NHibernate's Auto Persistence Model. However, on it's own it's pretty useless. We need to somehow tie it to a database (that doesn't exist yet) so that we can actually make use of it. To do so, we create a Mapper class that generates the mappings and returns the NHibernate configuration:



   1: public static class Mapper 
   2:    {
   3:  
   4:        public static Configuration GenerateMapping(string connectionString)
   5:        {
   6:            var config = MsSqlConfiguration.MsSql2005
   7:                .ConnectionString(c => c.Is(connectionString))
   8:                .ConfigureProperties(new Configuration());
   9:  
  10:            AutoPersistenceModel.MapEntitiesFromAssemblyOf<Customer>().Configure(config);
  11:  
  12:            return config;
  13:  
  14:        }
  15:  
  16:    }


What this class is doing, is create a NHibernate configuration that is based on Microsoft SQL Server (2005 and up) and configures a series of default properties. The only parameter it requires is the connection string. It generates the the mapping files and returns the configuration to us. Next step is to make use of this configuration. Remember, right now we have the mappings, but we don't actually have any underlying database. We don't know anything about how the structure is or should be.   


Creating the database

In the NHibernate framework, there is a namespace called hbm2ddl which as it's name indicates, can create DDL from mapping files. That is precisely what we need. To use it, it's as simple as passing in a NHibernate configuration to the SchemaExport class:  


1:
using NHibernate.Tool.hbm2ddl;
   2:  
   3: namespace Example.DBGenerator {
   4:  
   5:     
   6:     class Program 
   7:     {
   8:         static void Main(string[] args) 
   9:         {
  10:             var config = Mapper.GenerateMapping(args[0]);
  11:  
  12:             new SchemaExport(config).SetOutputFile(args[1]).Create(/* Output to console */ false, /* Execute script against database */ true);
  13:         }
  14:     }
  15: }


Ignoring the fact that there is absolutely no error checking in this application whatsoever, all we are doing is taking the our configuration generated by the mapper and passing it to our SchemaExport class, which is calling the Create method to create the SQL, dump it into a file and at the same time, generate it for us in SQL Server. Running this executable with the following command line:   

"Data Source=.\SQLEXPRESS;Initial Catalog=FluentExample;Integrated Security=true" Output.sql

 image


Summary

In this first post, I wanted to show you how you can easily create a domain model and have the corresponding database created for you automatically, without having to worry about any types of mappings, let alone database structures. However, I've only touched the tip of the iceberg. In the next series of posts, we'll drill down into everything Fluent-NHibernate has to offer us.

Coming back to the original comment, what do you think? Did I start with my domain object or my database? This is what true persistence ignorance is about!   

18 Comments Filed Under [ nhibernate ]

Comments

# re: Spending time with Fluent NHibernate: Part 1
Gravatar Nice post. AutoMappings rule.

The tweak model + generate db cycle is incredibly productive too. Have you got around to overriding certain auto-mappings yet, would be interesting to see examples (for example, how you make a particular relationship cascade-delete-orphan)
Left by Tobin Harris on 3/25/2009 10:38 AM
# re: Spending time with Fluent NHibernate: Part 1
Gravatar Hey Tobin

Thanks!

Yes I'm going to be covering overriding of conventions such as tweaking relationships, mapping components, naming conventions, etc, in succesive posts, but I thought it best to take it one step at a time. Easier to pick up IMHO.
Left by HHariri on 3/25/2009 10:49 AM
# re: Spending time with Fluent NHibernate: Part 1
Gravatar Great post Hadi, really informative!

I like all the options you get now - you can now let the requirements drive your implementation without being constrained by technology... Long gone are the days of DIY persistence frameworks, and amen to that!

Would be good if you could cover some tooling around generating the mapping files? Typing hurts my fingers :)
Left by Tom on 3/25/2009 12:25 PM
# re: Spending time with Fluent NHibernate: Part 1
Gravatar Thanks Tom. Actually there's a R# plugin that helps with mapping IIRC but you'll see in the next series of posts that with the APM, you hardly need any mapping :)
Left by HHariri on 3/25/2009 12:58 PM
# re: Spending time with Fluent NHibernate: Part 1
Gravatar Love it, more more more
Left by TomQ on 3/27/2009 6:22 PM
# Spending time with Fluent NHibernate (Part 2): Our first Relationship
Gravatar Spending time with Fluent NHibernate (Part 2): Our first Relationship
Left by Hadi Hariri's Blog on 3/30/2009 1:23 PM
# Zithromax.
Gravatar Zithromax. Zithromax antibiotics online. Zithromax 1g.
Left by Zithromax mexico. on 8/30/2009 4:18 AM
# Wellbutrin sr.
Gravatar Wellbutrin xl and results of. Wellbutrin.
Left by Cost wellbutrin. on 8/30/2009 10:38 AM
# Lexus ls.
Gravatar Lexus nexus. Lexus. Lexus cars. 2008 lexus lx470.
Left by Lexus. on 8/30/2009 7:45 PM
# Rx adipex.
Gravatar Adipex-p non-generic. Phentermine adipex. Adipex. Pillinc buy phentermine adipex meridia online. Adipex diet pills. Review adipex. Adipex phentermine. Cheap adipex.
Left by Rx adipex. on 8/31/2009 1:53 PM
# Seratim levitra pe.
Gravatar Why would he have levitra.. Online pharmacy cost levitra. Levitra viagra cyalis webmd comparisons. Levitra side effects. Levitra.
Left by Buy sublingual levitra online. on 8/31/2009 2:09 PM
# Zolpidem.
Gravatar Cheap zolpidem. Zolpidem overdose. Zolpidem. Zolpidem ambien.
Left by Zolpidem online. on 9/1/2009 12:09 PM
# Cialis.
Gravatar Order cialis add to cart.
Left by Buy cialis online. on 9/2/2009 5:30 AM
# Lipitor.
Gravatar Lipitor. Lipitor side effects.
Left by Side effects from lipitor. on 9/2/2009 5:43 AM
# Vicodin without a prescription.
Gravatar Liquid vicodin. Vicodin military urinalysis. No prescription needed vicodin.
Left by Vicodin. on 9/9/2009 6:42 AM
# program approximately home uncertain
Gravatar present suggests joint scientists
Left by program approximately home uncertain on 9/12/2009 4:17 PM
# continues beginning
Gravatar capita societies individual action reviews era
Left by continues beginning on 9/19/2009 9:07 PM
# upper mean
Gravatar offset long agree open projected environment industrial page
Left by upper mean on 9/27/2009 4:20 PM
Comments have been closed on this topic.