In this part of the series about NHibernate and its sister product Fluent NHibernate, Gabriel Schenker introduces the auto mapping
feature of Fluent NHibernate.
Your very first NHibernate application series
Part 1 In this series of articles Gabriel Schenker want to show and discuss which pieces are needed to successfully write an application based on NHibernate and Fluent NHibernate.Part 2 In this second part Gabriel Schenker continues his overview of the NHibernate OR/M.Part 3 In this part of the series about NHibernate and its sister product Fluent NHibernate, Gabriel Schenker introduces the auto mapping feature of Fluent NHibernate.
Introduction – Auto mapping the domain
In this part of the series about NHibernate and its sister product Fluent NHibernate I will introduce the auto mapping feature of Fluent
NHibernate. Using this feature the task of mapping a domain model to an underlying data model becomes a snap. The framework will do most of the work
for us and we only have to add our own mapping related code if we want to fine tune the mapping process.
The domain model
In the previous parts [1, 2] of this series I have introduced a simple but still useful domain model describing an order entry system. I have
shown how this model can be mapped to a database model with the aid of Fluent NHibernate [3]. In the following I will use FNH as an acronym for
Fluent NHibernate. The mapping has been done for each class of the domain. Although the mapping was very simple and straight forward as well as type
safe there is still one disadvantage; if we have a domain with many classes the manual mapping of them can become quite a burden.
Fortunately
there exists a solution: We can let FNH do the work for us and just let it automatically map the domain model to a corresponding data model. When
doing so FNH will analyze the classes defining the domain and use conventions to map them to underlying tables (and relations) in the database.
Although the conventions used by FNH are quite reasonable we can still apply our own conventions. These conventions are fine grained thus we have
extensive tuning possibilities.
Let’s again implement our (simple) domain model for an order entry system from scratch as simple as possible.
Here is the class diagram
Figure 1: Class diagram of the domain

And here
the code
Listing 1: Domain model
The above code probably reflects the simplest way to implement our domain model. I will later on extend improve the code as needed.
But for the moment this is enough.
Configuring auto mapping
Let’s now configure our system to automatically map our domain to a data model. We do this by using the fluent configuration API offered by FNH.
In my sample I’ll use two different databases to explicitly show how (Fluent) NHibernate produces database specific schemas. The first database is
SQLite [4] and the second one is SQL Server Compact Edition. Both are easy to set up and use and are file based.
Let’s start with the
configuration for SQLite. What I want to do is define a method that creates a session factory configured to use a SQLite database
Listing 2: Configuring NHibernate
I can use the class Fluently defined by FNH as well as one of the various predefined configuration classes. In our case it is the
SQLiteConfiguration class. I tell the system that I want to use SQLite in file mode using a file with the name provided by the constant
DbFile.
The Mappings(…) part of the configuration defines where or how the mapping between the domain model and the data model
is defined. In our class we want to let FNH do the hard work and use its auto mapping feature. FNH offers the class AutoPersitenceModel for this
purpose.
The mappings method expects a lambda expression. Our expression auto-creates mappings for all types found in the assembly where the
class Customer is defined but only those types whose namespace ends with “Domain”. If we would not have the where clause to filter the set of types
to map then FNH would try to just map every class in the assembly which is certainly not what we want.
If I want to use SQL Server Compact
Edition instead the only change I have to make in the above code is replacing the .Database(…) line with the following code
snippet
In this case I use the pre defined class MsSqlCeConfiguration and explicitly define a connection string to use. The above connection
string just instructs the database to use a file called AutomappingSample.sdf.
When using SQLite as database the schema generation script will
be the following
Listing 3: Schema generation script for SQLite
And for SQL Server compact edition we get this schema generation script (which is the same for SQL server)
Listing 4: Schema generation script for SQL Server
Please especially note the following
- To keep the script short the drop commands have been omitted
- The tables are named according to the respective classes
- The column names used correspond to the mapped property name
- The property
Id of each class is recognized as primary key of the respective entity/table
- On SQL Server identity fields are used as primary key.
- Properties of type string are mapped to NVARCHAR(255) in the case of SQL Server
- Foreign key columns are named according to the name of the referenced table combined with the postfix
_id.
- The names of the foreign key constraints (only SQL Server) are cryptic
This is the result of the default conventions that were applied during the generation of the database schema script. We can now start to
fine tune this by defining our own conventions.
Using user defined conventions
A convention is defined in a class that has to implement an interface pre defined by FNH. As I already told previously the conventions are really
fine grained thus loads of different interfaces are defined which we can implement if needed. As a simple sample let’s say that we want our
properties of type string to always be mapped to have a maximal length of 100 characters. Any of the possible convention interfaces to implement
ultimately inherits from the base interface IConvention<T> which defines the two methods Accepts(T target) and Apply(T
target). Thus we will have to implement exactly those two methods in our class.
Listing 5: User defined convention for properties of type string
The Accepts method defines when a convention can be applied and the Apply method contains the logic which
applies the convention to the given element. In the above case we accept any target that is of type string and in the Apply method we
define the maximal string length to be used in this case.
In a previous post I have mentioned that it is an anti-pattern to use identity
fields as primary keys for SQL Server in conjunction with NHibernate. It is preferable to e.g. use the HiLo generator if using numbers as primary
key; another alternative would be the usage of Guids as primary keys. For performance reasons use the GuidComb generator when using Guids as primary
keys. Having said this let’s define our own id convention which uses the HiLo generator instead of the identity generator. In this case we write a
class that implements the interface IIdConvention.
Listing 6: User defined convention for id
We want to apply this convention for any entity in our domain so we just return true in the Accept method. In the Apply method we
define which (id) generator we want to use. In this case it is the HiLo generator with an interval of 1000. Why we have to define the “maxLo”
value as string and not as a number is not clear; probably it’s a bug.
Now we have to tell the system to use these conventions instead of
the default conventions. We can do this either by explicitly add each convention to the configuration of NHibernate or by letting FNH scan an
assembly for implementations of conventions. Let’s start with the former. We can add a convention by using the ConventionDiscovery
method of the AutoPersistenceModel class.
Listing 7: Configuration adding user defined conventions
After modifying the configuration with our own conventions the schema generation script for SQL Server looks like this
Listing 8: Schema generation script for SQL Server after applying custom conventions
An additional table called hibernate_unique_key is created which is used for the management of the ids by NHibernate. Ids are no
longer columns of type identity but just of type int. Not SQL Server manages the ids now but NHibernate does it. Furthermore we can see that the
properties of type string are mapped to columns of type NVARCHAR(100).
Building the database schema
In the above configuration we use the ExposeConfiguration method which expects a delegate to a method that will be called during
the configuration process. In our case the delegate points to a method called BuildSchema. This method has one parameter of type
Configuration. We use the configuration object to create the database schema with the aid of the SchemaExport utility class of NHibernate.
Listing 9: Code needed to create the database schema
Note that in a productive environment you would omit this part since the database schema is generated only once; probably by a
DBA.
Using a base class for the entities
Our domain model has some weaknesses so far. Part of those are discussed in detail here [2] and here [5]. One weakness is that entities do not
implement the functionality do determine whether two instances represent the same entity. Equality is based on the id (primary key) of an entity. If
two instances have the same id then they are the same. A possible implementation is given in [5]. Since every entity has an id and every entity
needs to be comparable and to keep our code DRY it is best to define a base class for all entities which implements this common logic. Let’s call
our base class EntityBase. All entities will now inherit from this class. For further details please consult the code provided with this
article.
If we were to run this now, we wouldn't get the mapping we desire. FNH would see EntityBase as an actual entity and map it with all
other entities (e.g. Customer or Order) as subclasses; this is not what we desire, so we need to modify our auto mapping configuration to reflect
that.
After MapEntitiesFromAssemblyOf<EntityBase>() we need to alter the conventions that the auto mapper is using so it
can identify our base-class.
We've added the WithSetup call in which we replace the IsBaseType convention with our own. This convention is used to identify whether
a type is simply a base-type for abstraction purposes, or a legitimate storage requirement. In our case we've set it to return true if the type is
an EntityBase.
With this change, we now get our desired mapping. EntityBase is ignored as far as FNH is concerned, and all the properties (Id
in our case) are treated as if they were on the specific subclasses.
Summary
In part 3 of this article series about NHibernate and Fluent NHibernate I have discussed how to let Fluent NHibernate automatically map a domain
model to a data model. We have realized that FNH provides a reasonable mapping out of the box by using default conventions. I have shown how one can
implement user defined conventions which will influence how the mapping is defined on a very fine grained level. I have also shown that if we use a
base class in our domain which implements common functionality we can instruct FNH to ignore this class and just map the “real” entities.
FNH
with its auto mapping feature reduces the task of mapping a complex domain to an underlying data model to just a few keystrokes. And as is always
true: less code results in less bugs and less maintenance overhead.
References
About Gabriel Schenker
 |
Gabriel Schenker is a independent consultant, trainer, mentor and developer. He lives in Switzerland near to Zurich. His special focus is on practicing lean or friction-less software development. This includes practicing TDD, BDD and DDD. He is constantly advocating, promoting and supporting a cont...
This author has published 4 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Oredev Wrap-Up
read more
Introducing SharePoint 2010 Training at U2U
read more
Silverlight From Zero
read more
WPF 4 (VS 2010 and .NET 4.0 Series)
read more
A new approach to build iGoogle/Facebook like sites with Asp.Net: Kalitte Widget Toolkit
read more
Building A Product For Real
read more
My History of Visual Studio (Part 8)
read more
Microsoft announced the beta release of IIS Application Warm-Up
read more
My History of Visual Studio (Part 6)
read more
URL Routing with ASP.NET 4 Web Forms (VS 2010 and .NET 4.0 Series)
read more
|
|
Please login to rate or to leave a comment.