This is the third installment in my series. In part 1, we downloaded our libraries and set up our solution. In part 2, we built our model. In this part, we’ll configure NHibernate and set up our database mappings. We’ll also set up our database schema.
Java – A language of XML files loosely coupled by code.
Before we can talk about Fluent NHibernate, you need to know a little bit about setting up mappings in plain old NHibernate. In a typical NHibernate setup, you’ll have a bunch of mapping files like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="NStackExample.Address, NStackExample.Core" table="Address">
<composite-id>
<key-many-to-one name="Person" class="NStackExample.Person, NStackExample.Core" column="ID" />
<key-property name="Type" type="Int32" />
</composite-id>
<property name="City" type="String" length="255" />
<property name="Lines" type="String" length="255" />
<property name="State" type="String" length="2" />
<property name="Zip" type="String" length="10" />
</class>
</hibernate-mapping>
You’ll have one of those for each of your entities. It’s left over from Java’s Hibernate project, and in my opinion, It’s a royal pain, complete with ruby scepter. Lucky for you, there’s a better way™.
A Better Way™: Fluent Mappings
With Fluent NHibernate, the mapping file above can be expressed using this class instead:
Imports FluentNHibernate.Mapping
Public Class AddressMapping
Inherits ClassMap(Of Address)
Public Sub New()
UseCompositeId _
.WithKeyReference(Function(x As Address) x.Person) _
.WithKeyProperty(Function(x As Address) x.Type)
Map(Function(x As Address) x.Lines).WithLengthOf(255)
Map(Function(x As Address) x.City).WithLengthOf(255)
Map(Function(x As Address) x.State).WithLengthOf(2)
Map(Function(x As Address) x.Zip).WithLengthOf(5)
End Sub
End Class
using FluentNHibernate.Mapping;
namespace NStackExample.Data
{
public class AddressMapping : ClassMap<Address>
{
public AddressMapping()
{
UseCompositeId()
.WithKeyReference(x => x.Person)
.WithKeyProperty(x => x.Type);
Map(x => x.Lines).WithLengthOf(255);
Map(x => x.City).WithLengthOf(255);
Map(x => x.State).WithLengthOf(2);
Map(x => x.Zip).WithLengthOf(5);
}
}
}
It may look even more complicated than the XML mapping, but with Intellisense, it’s a breeze. Plus, there are no magic strings to worry about. When you change a property name using a refactor tool, your mapping won’t be left out of sync.
Now that you have the basic idea, let’s get back on track.
Where?
Since the database connection, NHibernate configuration, entity mappings, and DAO implementations are really just implementation details of our chosen ORM, they should go in a separate assembly.
- Make a new Class Library project called NStackExample.Data
- In the new Data project, add references to your core project, NHibernate.dll and FluentNHibernate.dll
- Add a reference to System.Configuration.dll so we can easily retrieve some application settings later.
- Also, the web project needs a reference to the data project.
Now, let’s make our mappings.
Imports FluentNHibernate.Mapping
Public Class CourseMapping
Inherits ClassMap(Of Course)
Public Sub New()
Id(Function(x As Course) x.ID).GeneratedBy.GuidComb()
Map(Function(x As Course) x.Subject).Not.Nullable.WithLengthOf(4).UniqueKey("CourseNaturalKey")
Map(Function(x As Course) x.CourseNumber).Not.Nullable.WithLengthOf(4).UniqueKey("CourseNaturalKey")
Map(Function(x As Course) x.Title).Not.Nullable.WithLengthOf(255)
Map(Function(x As Course) x.Description).Not.Nullable.WithLengthOf(1024)
Map(Function(x As Course) x.Hours).Not.Nullable()
HasMany(Function(x As Course) x.Sections) _
.AsSet() _
.WithForeignKeyConstraintName("CourseSections")
End Sub
End Class
using NStackExample;
using FluentNHibernate.Mapping;
namespace NStackExample.Data
{
public class CourseMapping : ClassMap<Course>
{
public CourseMapping()
{
Id(x => x.ID).GeneratedBy.GuidComb();
Map(x => x.CourseNumber)
.Not.Nullable()
.WithLengthOf(4)
.UniqueKey("CourseNaturalKey");
Map(x => x.Subject)
.Not.Nullable()
.WithLengthOf(4)
.UniqueKey("CourseNaturalKey");
Map(x => x.Title)
.Not.Nullable()
.WithLengthOf(255);
Map(x => x.Description)
.Not.Nullable()
.WithLengthOf(1024);
Map(x => x.Hours)
.Not.Nullable();
HasMany(x => x.Sections)
.AsSet()
.WithForeignKeyConstraintName("CourseSections");
}
}
}
Most of this is self-explanatory and works exactly like you would expect.
Our mapping class inherits from ClassMap(Of Course). ClassMap is the specific type that Fluent NHibernate searches for when looking for mappings. In this case, it signifies that this class provides the mapping for our Course entity. In the constructor, we define our specific mapping for each property.
- Id sets up the persistent object identifier (POID). This is basically the primary key for the table. If you have more than one property in the primary key, as in the case of natural keys, go with UseCompositeId like in the address example above. Using multi-part keys isn’t really suggested and to my knowledge, isn’t fully supported by Fluent NHibernate.
- GeneratedBy specifies the POID generator. How will you assign your keys? In my case, I use GuidComb. I get all of the benefits of guid identifiers, but I don’t fragment my database index nearly as much. You can read up on it more in Davy Brion‘s post on the NHForge blog.
- Map simply maps a property to a database column. You can specify Not.Nullable and WithLengthOf as necessary.
- UniqueKey specifies a unique index on the column. If you specify the same name on several columns, all of those columns will be part of the same unique index. In this example, we are forcing our natural key to be unique. Each combination of subject and course number must be unique. There can only be one ENGL 1301 course. Thank goodness.
- HasMany defines a one-to-many relationship. You can specify the exact behavior of the collection. You have several options here, but the two types I use almost exclusively are Set and Bag.
- AsSet doesn’t allow duplicate items.
- With AsBag, duplicates are allowed.
By default, all relationships are lazy-loaded. This means that when you fetch a course from the database, the associated sections aren’t fetched right away. It works just like you would expect: They aren’t fetched until you access the Sections property. If you never access the Sections property, those sections are never fetched from the database, which can greatly improve performance. This is all made possible with proxies, but that’s another series of posts.
Now let’s map the sections:
Imports FluentNHibernate.Mapping
Public Class SectionMapping
Inherits ClassMap(Of Section)
Public Sub New()
Id(Function(x As Section) x.ID).GeneratedBy.GuidComb()
Map(Function(x As Section) x.FacultyName).WithLengthOf(255)
Map(Function(x As Section) x.RoomNumber).WithLengthOf(10)
Map(Function(x As Section) x.SectionNumber) _
.WithLengthOf(4) _
.Not.Nullable() _
.UniqueKey("SectionNaturalKey")
References(Function(x As Section) x.Course) _
.Not.Nullable() _
.UniqueKey("SectionNaturalKey")
References(Function(x As Section) x.Term) _
.Not.Nullable() _
.UniqueKey("SectionNaturalKey")
HasMany(Function(x As Section) x.StudentSections) _
.AsSet() _
.WithForeignKeyConstraintName("SectionStudentSections")
End Sub
End Class
The References function maps the Many-to-one relationship. Think of it as the other side of our one-to-many relationship. It is the reference from the child – section – back to it’s parent – course.
For homework, finish mapping all of the entities.
I bet you’re thinking this post is getting long considering we haven’t even started building the database. Well don’t worry. NHibernate will do that for us.
8 hours or 8 minutes?
Before I discovered NHibernate, I would spend at least a day setting up my database. It was insane. It drove me insane. I bet it drives you insane. It ends today.
Disclaimer: If you are trying to use an existing shared legacy database, the chances of your existing DB schema working without some tweaking are slim. This post by Fabio Maulo explains your options.
First, let’s configure NHibernate. The Fluent NHibernate Wiki has a great page explaining the fluent configuration of NHibernate.
Imports NHibernate
Imports NHibernate.Tool.hbm2ddl
Imports FluentNHibernate.Cfg
Imports System.Configuration
Imports System.IO
Public Class Configuration
Private m_SchemaPath As String
Private m_Factory As ISessionFactory
Public Function Configure() As Configuration
m_SchemaPath = ConfigurationManager.AppSettings("NStackExample.Data.Configuration.SchemaPath")
m_Factory = Fluently.Configure _
.Database(Db.MsSqlConfiguration.MsSql2005 _
.ConnectionString(Function(x As Db.MsSqlConnectionStringBuilder) _
x.FromConnectionStringWithKey("NStackExample.Data.Configuration.DB"))) _
.Mappings(Function(x As MappingConfiguration) _
x.FluentMappings.AddFromAssemblyOf(Of CourseMapping)() _
.ExportTo(m_SchemaPath)) _
.ExposeConfiguration(AddressOf BuildSchema) _
.BuildSessionFactory()
Return Me
End Function
Private Sub BuildSchema(ByVal Cfg As NHibernate.Cfg.Configuration)
Dim SchemaExporter As New NHibernate.Tool.hbm2ddl.SchemaExport(Cfg)
SchemaExporter.SetOutputFile(Path.Combine(m_SchemaPath, "schema.sql"))
SchemaExporter.Create(False, True)
End Sub
Public Function OpenSession() As ISession
If m_Factory Is Nothing Then Configure()
Return m_Factory.OpenSession
End Function
End Class
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using System.IO;
using System.Configuration;
namespace NStackExample.Data
{
public class Configuration
{
private ISessionFactory m_Factory;
private string m_SchemaPath;
public Configuration Configure()
{
m_SchemaPath = ConfigurationManager.AppSettings["NStackExample.Data.Configuration.SchemaPath"];
m_Factory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005
.ConnectionString(
x => x.FromConnectionStringWithKey("NStackExample.Data.Configuration.Db")))
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<CourseMapping>()
.ExportTo(m_SchemaPath))
.ExposeConfiguration(BuildSchema)
.BuildSessionFactory();
return this;
}
private void BuildSchema(NHibernate.Cfg.Configuration cfg)
{
SchemaExport SchemaExporter = new SchemaExport(cfg);
SchemaExporter.SetOutputFile(Path.Combine(m_SchemaPath, "schema.sql"));
SchemaExporter.Create(true, false);
}
public ISession OpenSession()
{
if (m_Factory == null) Configure();
return m_Factory.OpenSession();
}
}
}
The configuration falls in to two sections: Database and Mappings. In our case, the database is SQL 2005 and the connection string is read from a connection string element in the web.config. All of the mappings are fluent, not auto-mapped. Notice that we are exporting our mappings to a directory specified in the appsettings section of the web.config. This will convert our fluent mappings to individual hbm.xml files. This is great for debugging the mappings, especially when asking for NHibernate help online.
We have one additional item. We’re using the ExposeConfiguration method to call our BuildSchema function, passing in our complete NHibernate configuration.
In BuildSchema, we use a great hidden tool in NHibernate: the schema export. This amazing class will build your database for you. The create function takes two boolean parameters. The first specifies if the schema should be written out to a ddl file – a database script to build all of the tables, keys, indexes, and relationships in your database. The second boolean parameter specifies if the script should be executed against the specified database.
It’s that easy.
Two warnings:
- Executing this script will drop and recreate every table associated with your model. That can be devastating in a production environment.
- The script doesn’t start with a a “use [databasename]” statement, so if you’re not careful, when you execute it, you’ll build everything in the master database.
One last note: As with any project, you will have to adapt as you build. These mappings are not exactly what we use in the final build. I can guarantee our model will change significantly. I will take you through those changes as they happen, and explain the reasons behind them.
I’ve decided not to post the complete source code at this stage. Instead, I leave the remaining mappings as an exercise for you, the reader. They will be included in the next source release.
In the next post, I’ll show you how to test your mappings – including querying, reading from and writing to the database.
Jason
- Mapped out. Good night.
P.S. – Special thanks to Tuna, Fabio, and Oren for the feedback, answers to stupid questions, and great advice!




#1 by marek on August 14th, 2009
Hi Jason,
thanks for this series of posts. Great choice of tools and technology. Looking forward for next articles.
Thanks!
Marek
Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #412
#2 by Scott Hanselman on August 14th, 2009
Great series! Keep it up!
#3 by Jason on August 14th, 2009
Wow. Thanks for the compliment! Coming from the guy on the covers of my favorite Wrox books, that’s a real honor. The Windows 7 Boot to VHD thing is pure awesome. It’s worth the upgrade price for that feature alone.
#4 by Yassir on August 14th, 2009
Is it ok to make mapping classes sealed ?
#5 by Jason on August 14th, 2009
I did a quick test. It seems to work, but I can’t think of a good reason why it would be necessary to do that.
Pingback: NHibernate Talk 8/14/09 - Travis.Net.Blog
Pingback: Page not found « BASICly everything
#6 by Yassir on August 16th, 2009
Type your comment here
because you have a virtual member call in .ctor for now it is ok but if someone Inherites from one of the mapping class you might run to problems
#7 by Jason on August 16th, 2009
Remember, you are only loading up the mappings declared in the Data assembly, so it would have to be an inside job. It can’t be done from outside the assembly without some major System.Reflection hacking.
No framework will protect you from yourself or your coworkers.
#8 by José F. Romaniello on August 16th, 2009
I will strongly recommend three things:
1-If you are developing a app from scratch (I mean no legazy-db) avoid composite primary keys.
2- Your xml-mapping is too much verbose:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NStackExample"
namespace="NStackExample">
<class name="Address">
<composite-id>
<key-many-to-one name="Person" class="Person" column="ID" />
<key-property name="Type" type="Int32" />
</composite-id>
<property name="City" length="255" />
<property name="Lines" length="255" />
<property name="State" length="2" />
<property name="Zip" length="10" />
</class>
</hibernate-mapping>
3-Address in this case seems to be a Component.
#9 by Jason on August 16th, 2009
That was just an example I pulled from another project – a brownfield project. The mapping was generated by fluent nhibernate and designed to fit an existing DB schema.
Pingback: ASP.NET MVC Archived Buzz, Page 1
#10 by José F. Romaniello on August 18th, 2009
Ok, But the problem is that when you say “a better way..” you are contrasting “fluent nhibernate generated mappings” with “fluent nhibernate”. Let me say that fluent nhibernate mappings aren’t human readable.
#11 by Jason on August 18th, 2009
Yes. I did compare an FNH generated xml mapping to FNH code. You’ve stripped out some property types and assembly-qualified type names, and it is cleaner. If I had one handy, I probably would have used a hand-written mapping from a greenfield project. Looking back, it does come across as a dishonest comparison.
I do agree that hand-written XML mappings are usually more readable than those generated by FNH, but as I understand it, that’s not the goal of the FNH project.
I really just have two points:
#1 – Even in their simplest form, XML mappings are still more difficult to read, and more importantly, more difficult to maintain than an FNH mapping written in C#. Of course, to a degree, readability and even maintainability are subjective.
I don’t agree with the “enterprise Java” point of view that XML is the perfect solution for everything. I think you pay a VERY heavy “magic strings” tax with XML in general, and especially when a lot of XML has to stay in sync with a lot of code, as in the case of mappings. In one of the most critical locations in the applications, that’s just unacceptable – again, just my opinion.
XML has it’s place. It’s great for standard communications protocols and configuration settings that are likely to change as the hardware and network change. In the case of mappings, it’s being used more as a replacement for code. It feels wrong to me.
#2 – XML mappings will slow the adoption of NHibernate. FNH is a success partly because it abuses the hell out of Intellisense to flatten out the NH learning curve.
I’m not bashing NHibernate. It’s an amazing ORM, and the XML mappings are part of its history. They also make a great integration point for codegen tools. I think as NHibernate is adopted outside of the ALT.NET community, you’ll see a lot less hand-written mappings and a lot more codegen, DB reverse-engineer, and FNH mappings.
In case it wasn’t obvious already, I’m pretty new to NHibernate. I’m still making rookie mistakes. The learning curve for NHibernate for a newbie in a “hurry up and get it done” drag-n-drop programming shop is pretty steep. Add in these weird little XML mappings that refuse to stay in sync with the model and the newbie might just choose EF and its GUI designers over NHibernate.
Now that FNH is officially v1 RC, NH could serve itself well to push FNH to beginners as an easy one-size-fits-most mapping solution while still supporting the XML mappings for advanced fine-tuning. But that’s for Fabio to decide, not me.
#12 by José F. Romaniello on August 18th, 2009
I agree with you.
#13 by Norbert Beckers on August 19th, 2009
This series isn’t up to date. I’m using 1.0RC, the latest release version of FluentNHibernate. The “WithLengthOf(Int32)” method is replaced by “Length(Int32)”
#14 by Norbert Beckers on August 19th, 2009
Update: “WithForeignKeyConstraintName(String)” now is “ForeignKeyConstraintName(String)”
#15 by Jason on August 19th, 2009
@Norbert – v1 RC was released less than 48 hours ago. I’ll include those updates in my next post. Thanks.
#16 by Norbert Beckers on August 20th, 2009
No Thanks. Changes go fast I reckon…;-}~
#17 by Rob Bihun on August 19th, 2009
In your configuration.cs (line # 26 in this post) you have:
.Mappings(x => x.FluentMappings.AddFromAssemblyOf()
.ExportTo(m_SchemaPath))
Is this better than:
.Mappings(x =>
x.FluentMappings.AddFromAssembly(Assembly.GetAssembly(this.GetType())).ExportTo(m_SchemaPath))
Forgive me if this is an ignorant question, I am just now starting to play with NHibernate. To me your post (before looking closely) looks like you are doing something specifically with the CourseMapping class and not all the classes that inherit ClassMap… which in this case are all contained in NStackExample.Data along with the configuration class.
#18 by Jason on August 19th, 2009
@Rob – Functionally, they are identical. Either will work.
Personally, I prefer the shorter AddFromAssemblyOf method. The method name indicates that you are dealing with the assembly containing the type, not the type specifically.
Pingback: Part 5: Fixing the Broken Stuff « BASICly everything
Pingback: Part 5: Fixing the Broken Stuff - NHibernate blog - NHibernate Forge