Published: 01 Sep 2009
By: Gabriel Schenker
Download Sample Code

In this part of the series about NHibernate and its sister product Fluent NHibernate, Gabriel Schenker introduces the auto mapping feature of Fluent NHibernate.

Contents [hide]

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

    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

    <<  Previous Article Continue reading and see our next or previous articles Next Article >>

    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.

    Other articles in this category


    A Feature-driven Comparison of Entity Framework and NHibernate-2nd Level Caching
    Where would you place caching in your layered solution? Two main patterns exist for caching in appli...
    A Feature-driven Comparison of Entity Framework and NHibernate - Queries
    Let's explore what Entity Framework and NHibernate has to offer when it comes to their query capabil...
    A Feature-driven Comparison of Entity Framework and NHibernate—Fetch Plans
    This article is about fetch plans-a recognized and common way for developers to instruct the O/RM ab...
    A Feature-driven Comparison of Entity Framework and NHibernate - Self-tracking entities
    In this article, Dino Esposito introduces self-tracking entities.
    .NET type generation for NHibernate mapping collections
    An overview of .NET type generation for NHibernate mapping collections.

    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
    Top
     
     
     

    Discussion


    Subject Author Date
    placeholder Broken final links Luca Del Tongo 9/2/2009 6:37 AM
    RE: Broken final links Sonu Kapoor 9/2/2009 8:27 AM
    placeholder Oracle Emil Yankov 9/11/2009 11:58 AM
    great Happy Reyes 5/28/2010 3:07 PM

    Please login to rate or to leave a comment.