How to use Fluent NHibernate with auto mappings

For this article, I’ll be modifying “Your First Project” found on the Fluent NHibernate Wiki: Getting Started page. I’ll also borrow a couple of ideas from FlashM’s article on Dream.In.Code. The idea is to take advantage of the auto mappings feature of Fluent NHibernate, which is not documented very well, even though it’s arguably its strongest selling point.

Here’s what the completed project will look like:

To begin, create a new console application called FluentNHibernateConsole in Microsoft Visual Studio 2010. Next, right click on the project in the Solution Explorer and choose Manage NuGet Packages… Search the online gallery for Fluent NHibernate and install it.

EDIT 12/18/2012: I finally managed to get the source code on GitHub!

Next, create a new folder in your project and name it Entities. Place the following classes in your Entities folder:

namespace FluentNHibernateConsole.Entities
{
    public class Employee
    {
        public virtual int Id { get; protected set; }
        public virtual string FirstName { get; set; }
        public virtual string LastName { get; set; }
        public virtual Store Store { get; set; }
    }
}

Before Fluent NHibernate version 1.3.0.717, we could make the setter function of the Id property private instead of protected. The reason it needs to be one or the other is because it should never be changed by the code. Instead, it should only ever be touched by NHibernate.

namespace FluentNHibernateConsole.Entities
{
    public class Product
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
        public virtual double Price { get; set; }
        public virtual IList<Store> StoresStockedIn { get; protected set; }

        public Product()
        {
            StoresStockedIn = new List<Store>();
        }
    }
}

Not only should the Id property of the Product class be protected, but the StoresStockedIn property should be as well. Once this list is populated, we should never be able to set it since that would create orphaned Store records.

namespace FluentNHibernateConsole.Entities
{
    public class Store
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
        public virtual IList<Product> Products { get; protected set; }
        public virtual IList<Employee> Staff { get; protected set; }

        public Store()
        {
            Products = new List<Product>();
            Staff = new List<Employee>();
        }

        public virtual void AddProduct( Product product )
        {
            product.StoresStockedIn.Add( this );
            Products.Add( product );
        }

        public virtual void AddEmployee( Employee employee )
        {
            employee.Store = this;
            Staff.Add( employee );
        }
    }
}

Here’s an outline of the Program class:

namespace FluentNHibernateConsole
{
    public class Program
    {
        // Adds sample data to our database and writes the data to the console
        static void Main() { }

        // Returns our session factory
        private static ISessionFactory CreateSessionFactory() { }

        // Returns our database configuration
        private static MsSqlConfiguration CreateDbConfig()

        // Returns our mappings
        private static AutoPersistenceModel CreateMappings() { }

        // Drops and creates the database schema
        private static void DropCreateSchema( Configuration cfg) { }

        // Writes the store data, along with its associated products and employees, to the console
        private static void WriteStorePretty( Store store ) { }

        // Adds any products that we pass in to the store that we pass in
        public static void AddProductsToStore( Store store, params Product[] products ) { }

        // Adds any employees that we pass in to the store that we pass in
        public static void AddEmployeesToStore( Store store, params Employee[] employees ) { }
    }
}

WriteStorePretty, AddProductsToStore and AddEmployeesToStore methods are all fairly self-explanatory:

// Writes the store data, along with its associated products and employees, to the console
private static void WriteStorePretty( Store store )
{
    Console.WriteLine( store.Name );
    Console.WriteLine( " Products:" );

    foreach ( var product in store.Products )
    {
        Console.WriteLine( " " + product.Name );
    }

    Console.WriteLine( " Staff:" );

    foreach ( var employee in store.Staff )
    {
        Console.WriteLine( " " + employee.FirstName + " " + employee.LastName );
    }

    Console.WriteLine();
}

// Adds any products that we pass in to the store that we pass in
public static void AddProductsToStore( Store store, params Product[] products )
{
    foreach ( var product in products )
    {
        store.AddProduct( product );
    }
}

// Adds any employees that we pass in to the store that we pass in
public static void AddEmployeesToStore( Store store, params Employee[] employees )
{
    foreach ( var employee in employees )
    {
        store.AddEmployee( employee );
    }
}

In the Main method, we open a session factory, then begin a transaction. At the end of the transaction, we save the session and commit the transaction:

// Adds sample data to our database and writes the data to the console
static void Main()
{
    // Create a session factory
    var sessionFactory = CreateSessionFactory();

    // Open a session
    using ( var session = sessionFactory.OpenSession() )
    {
        // Begin a transaction
        using ( var transaction = session.BeginTransaction() )
        {
            // Create a couple of Stores each with some Products and Employees
            var barginBasin = new Store { Name = "Bargin Basin" };
            var superMart = new Store { Name = "SuperMart" };

            var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
            var fish = new Product { Name = "Fish", Price = 4.49 };
            var milk = new Product { Name = "Milk", Price = 0.79 };
            var bread = new Product { Name = "Bread", Price = 1.29 };
            var cheese = new Product { Name = "Cheese", Price = 2.10 };
            var waffles = new Product { Name = "Waffles", Price = 2.41 };

            var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
            var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
            var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
            var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
            var joan = new Employee { FirstName = "Joan", LastName = "Pope" };

            // Add Products to the Stores
            // The Store-Product relationship is many-to-many
            AddProductsToStore( barginBasin, potatoes, fish, milk, bread, cheese );
            AddProductsToStore( superMart, bread, cheese, waffles );

            // Add Employees to the Stores
            // The Store-Employee relationship is one-to-many
            AddEmployeesToStore( barginBasin, daisy, jack, sue );
            AddEmployeesToStore( superMart, bill, joan );

            // Save the session
            session.SaveOrUpdate( barginBasin );
            session.SaveOrUpdate( superMart );

            // Commit the transaction
            transaction.Commit();
        }

        // Begin a transaction
        using ( var transaction = session.BeginTransaction() )
        {
            var stores = session.CreateCriteria( typeof( Store ) )
                .List();

            foreach ( var store in stores )
            {
                WriteStorePretty( store );
            }

            // Commit the transaction
            transaction.Commit();
        }
    }

    Console.ReadKey();
}

This hard dependency on NHibernate isn’t a best practice, but it will do for now.

The CreateSessionFactory method is where we get into the configuration of Fluent NHibernate:

// Returns our session factory
private static ISessionFactory CreateSessionFactory()
{
    return Fluently.Configure()
        .Database( CreateDbConfig )
        .Mappings( m => m.AutoMappings.Add( CreateMappings() ) )
        .ExposeConfiguration( DropCreateSchema )
        .BuildSessionFactory();
}

There are four steps to our fluent configuration. First, we configure our database connection with a call to the CreateDbConfig method:

// Returns our database configuration
private static MsSqlConfiguration CreateDbConfig()
{
    return MsSqlConfiguration
        .MsSql2008
        .ConnectionString( c => c
            .Server( "testServer" )
            .Database( "testDB" )
            .Username( "testUser" )
            .Password( "testPass" );
}

Before running the application, make sure you create an empty database, and replace “testServer”, “testDB”, “testUser” and “testPass” with your actual server, database, username, and password. Note that I’m using SQL Server 2008, but you can use SQLite, MySQL, or any other supported database. You can also read the connection string from a settings file, which you can read more about on the Fluent NHibernate Wiki.

Next, we configure our mappings with a call to the CreateMappings method:

// Returns our mappings
private static AutoPersistenceModel CreateMappings()
{
    return AutoMap
        .Assembly( System.Reflection.Assembly.GetCallingAssembly() )
        .Where( t => t.Namespace == "FluentNHibernateConsole.Entities" )
        .Conventions.Setup( c => c.Add( DefaultCascade.SaveUpdate() ) );
}

Normally, NHibernate uses XML files to determine how each entity in the domain model relates to each table in the database. Fluent NHibernate allows us to bypass this laborious step with auto mappings. We can add other conventions here, but all we need to get our application running is to turn cascading on. This way, when we save a Store, all the associated Products and Employees are saved too.

Next, we tell NHibernate to drop and create our database schema each time the application runs:

// Drops and creates the database schema
private static void DropCreateSchema( Configuration cfg )
{
    new SchemaExport( cfg )
        .Create( false, true );
}

This step is optional. If you prefer, you can the database schema manually. You can also tell NHibernate to update the schema only when the model changes:

// Updates the database schema if there are any changes to the model
private static void UpdateSchema( Configuration cfg )
{
    new SchemaUpdate( cfg );
}

Finally, we build the session factory. This is the last step in our fluent configuration.

We can now run the application. When you do so, you should see this:

If this is what you see, then congratulations on completing your first Fluent NHibernate project with auto mappings! If not, drop me a comment and we’ll try to get it figured out. I’m pretty new to this too, so don’t be afraid to ask!

Advertisements

7 thoughts on “How to use Fluent NHibernate with auto mappings

    1. Dave Post author

      You can try this:

      // Returns our NHibernate session factory
      private static ISessionFactory CreateSessionFactory()
      {
          var mappings = CreateMappings();
      
          return Fluently
              .Configure()
              .Database( MsSqlConfiguration.MsSql2008
                  .ConnectionString( "Server=localhost\\SQLEXPRESS;Database=testDB;Integrated Security=SSPI;" ) )
              .Mappings( m => m
                  .AutoMappings.Add( mappings ) )
              .ExposeConfiguration( BuildSchema )
              .BuildSessionFactory();
      }
      

      I think “Integrated Security=True” will also work. See http://www.connectionstrings.com/sql-server-2008 for more info.

      Reply
    2. Dave Post author

      I finally ran into a situation where I needed to use Windows Authentication. I had to pull up this thread because I forgot how to do it :)

      Anyways, I can verify that “Integrated Security=True” does work as well as “Trusted_Connection=True”. Instead of passing the raw connection string, you could do this:

      // Returns our NHibernate session factory
      private static ISessionFactory CreateSessionFactory()
      {
          var mappings = CreateMappings();
      
          return Fluently
              .Configure()
              .Database( MsSqlConfiguration.MsSql2008
                  .ConnectionString( c => c
                      .Server( "localhost\\SQLEXPRESS" )
                      .Database( "testDB" )
                      .TrustedConnection() ) )
              .Mappings( m => m
                  .AutoMappings.Add( mappings ) )
              .ExposeConfiguration( BuildSchema )
              .BuildSessionFactory();
      }
      
      Reply
  1. Raymond

    Hi Dave,

    Nice tutorial!
    I just have a question, I try to move the folder ‘Entities’ into a different project which I called ConsoleApplication1.Models.
    When I run the application, it throws up an error. The ‘CreateMappings’ function cannot create the mappings anymore. I did change the namespace to ‘ConsoleApplication1.Models.Entities’.
    return AutoMap
    .Assembly( System.Reflection.Assembly.GetCallingAssembly() )
    .Where( t => t.Namespace == “ConsoleApplication1.Model.Entities” )
    .Conventions.Setup( c =>
    {
    c.Add( DefaultCascade.SaveUpdate() );
    } );

    Can you advise how I can fix this error?
    Thank you.

    Reply
    1. Dave Post author

      Could it be a simple typo? I think ConsoleApplication1.Model.Entities should be ConsoleApplication1.Models.Entities

      Also, make sure you didn’t forget to change the namespace in each file that you moved.

      Reply
  2. Richar

    Hello Dave, nice tutorial, I’m working with an mvc5 app so I try to apply automappings but It returning this error:

    The entity ‘BundleConfig’ doesn’t have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).

    I have no idea why, because bundlesconfig entity have nothing to do in this context.

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s