Click here to Skip to main content
Click here to Skip to main content

Fluent NHibernate and Linq2NHibernate – Demo Project

By , 2 Jul 2009
 

Introduction

This is my so long awaited demo project showcasing the use of Fluent NHibernate and Linq to NHibernate (and some other interesting bits).

First of all, if you are completely new to NHibernate, I encourage you to take a look at my previous introductory article here.

Disclaimer

Before commencing, I want to make a disclaimer about this project, as you may expect, this is just demo code, not intended to be taken too seriously, please use it just as a *soft* guide of how very basic things can be done using NHibernate. However, I've tried to do my best implementing the code for this article, with time limitations and all the related issues that an active developer has.

Prerequisites

  • .NET Framework 3.5 SP1
  • Visual Studio 2008
  • To run the test from inside Visual Studio: TestDriven.Net
  • MSSQL Server 2005 with the AdventureWorks database installed
  • All other dependencies (assemblies) are included within the solution

Let's Go

What is this about? Take a look at the solution structure:

Solution-1

I'm going to explain what’s the purpose of each of those projects.

NHibernateSample.Model

This is the simplest one. Here I've created the classes that are going to be mapped against our database, those are just POCO classes that represent a view of the real database. Take a look at this image:

ModelDiagram

Each class must override its GetHashCode method (which should be implemented in a way that returns unique results for each unique entity) and its Equals method in order to allow NHibernate to handle the loading and session caching process of entities. Those methods look similar in almost all the cases, except when we are handling entities that have composite ids.
Let's take a look at how this is being done in the AddressType class that has its AddressTypeID property as the primary key:

public override int GetHashCode()
{
    return HashCodeGenerator.GenerateHashCode(AddressTypeID);
}
public override bool Equals(object obj)
{
    return this.IsEqual(obj);
}

The GenerateHashCode method from the HashCodeGenerator class (NHibernateSample.Model/Helper/HashCodeGenerator.cs) and it looks like:

public static class HashCodeGenerator
{
    public static int GenerateHashCode(params object[] keys)
    {
        int hash = 17;

        foreach (var item in keys)
        {
            int itemHashCode;
            if (item == null)
            {
                itemHashCode = 1;
            }
            else
            {
                itemHashCode = item.GetHashCode();
            }
            hash = hash * 23 + itemHashCode;
        }

        return hash;
    }
} 

As you can see, nothing glamorous, I'm just assuring that the result of the method is unique.

The override of the Equals method is using the IsEqual extension method from the EqualityHelper class (NHibernateSample.Model/Helper/EqualityHelper.cs) and it looks like:

public static class EqualityHelper
{
    public static bool IsEqual<T>(this T source, object obj) where T : class
    {
        var target = obj as T;
        if (obj == null)
        {
            return false;
        }
        return target.GetHashCode().Equals(source.GetHashCode());
    }
}

As you can see, it only checks the equality of the method GetHashCode of the instances being passed to the method, simple enough.

NHibernateSample.ModelMapper

This is a very important project. Here is where I'm using the Fluent NHibernate API to map our NHibernateSample.Model assembly against our database.
One of the cool things that the Fluent API provides to us is the use of conventions (following the “convention over configuration” spirit) that helps us to save a lot of time in the mapping process, for example, you may have a guideline for your database with regards to naming the primary key column of a given table, something like <Table_Name>ID, so, for the Product table you have ProductID table.
Using plain NHibernate mapping files, you would have to go into the tedious work of mapping each of one entity, throwing away your convention.
Hopefully we are using Fluent NHibernate and this is easy cake.

This project has few classes that inherit from FluentNHibernate.Mapping.ClassMap which gives us all the facilities to configure our entities.
Let's take a look at our StateProvinceMapper class:

public class StateProvinceMapper : ClassMap<StateProvince>
{
    private const string schema = "Person";
    public StateProvinceMapper()
    {
        SchemaIs(schema);
        Id(x => x.StateProvinceID);
        Map(x => x.Name)
            .WithLengthOf(50)
            .ReadOnly();
        References(x => x.TerritoryID)
            .LazyLoad()
            .Not.Nullable();
    }
}

As you can see, this is pretty straightforward.

  1. We set the schema of this entity, which is the “Person” schema in the AdventureWorks database.
  2. We set the Id of this entity, which is the StateProvinceID property that maps directly to the same column in the database table.
  3. We map our Name property against the Name column in the database table, also, we set some attributes as its MaxLenght and also we say that this is a readonly property.
  4. We set a reference with another entity using our TerritoryID property (of StateProvince type, which also maps to its corresponding table), also we set the LazyLoad attribute that says to NHibernate to do not retrieve it from the database until an explicit request is performed (stateProvinceInstance.TerritoryID.Name would fire a database query) and lastly we mark this property as Not-Nullable so in Update and Insert operations, an attempt to store an StateProvince entity with a null value for its TerritoryID property will throw an exception.

The place in charge of building the mapping from our Model against the database is the ModelBuilder class (NHibernateSample.ModelMapper/ModelBuilder.cs), in there we are doing all the heavy work to create our mapping:

private static void buildModel()
{
    // initialize persistence configurer
    IPersistenceConfigurer persistenceConfigurer = getPersistenceConfigurer();
    // initialize nhibernate with persistence configurer properties
    cfg = persistenceConfigurer.ConfigureProperties(new Configuration());
    // add mappings definition to nhibernate configuration
    var persistenceModel = new PersistenceModel();
    persistenceModel.addMappingsFromAssembly(typeof(ModelBuilder).Assembly);
    persistenceModel.Conventions.GetPrimaryKeyName = x => x.Name;
    persistenceModel.Conventions.GetForeignKeyName = x => x.Name;
    persistenceModel.Configure(cfg);

    //Mix mode, this line allows us to append config settings to our configuration
    //instance from .config files (web.config,app.config)
    // or any other "traditional" (xml based) approach.
    //NOTE: If you are going to go with full Fluent configuration, 
    //you should remove the line below
    cfg.Configure();
}

Look how we are saying that the GetPrimaryKeyName should be inferred directly from the property name and the same for the GetForeignName method, so, if we have a class Product, and we say that its ID is ProductID, its primary key should be mapped to the column ProductID as well, and the same for its references.

You can look deeper into the project to get a more detailed view of what is going on there.

NHibernateSample.LINQModel

In this project, we are using the NHibernate.Linq assembly to create a NHibernateContext which has implemented a LINQ provider (not as good as LINQ to SQL) that allows us to create queries in a LINQ to SQL fashion.
The main class there is the ModelContext class, which takes our entities and exposes them as IOrderedQueryable implementations.
It is very simple, here is how it looks:

public class ModelContext : NHibernateContext
{
    public ModelContext(ISession session)
        : base(session)
    {
    }
    public IOrderedQueryable<Customer> Customers
    {
        get
        {
            return Session.Linq<Customer>();
        }
    }
    public IOrderedQueryable<Address> Addresses
    {
        get
        {
            return Session.Linq<Address>();
        }
    }

    public IOrderedQueryable<AddressType> AddressTypes
    {
        get
        {
            return Session.Linq<AddressType>();
        }
    }

    public IOrderedQueryable<CustomerAddress> CustomerAddresses
    {
        get
        {
            return Session.Linq<CustomerAddress>();
        }
    }

    public IOrderedQueryable<SalesTerritory> SalesTerritories
    {
        get
        {
            return Session.Linq<SalesTerritory>();
        }
    }

    public IOrderedQueryable<StateProvince> StateProvincies
    {
        get
        {
            return Session.Linq<StateProvince>();
        }
    }
}

As you can see, it takes an ISession (which is our NHibernate instance to handle the database management) and for each entity, I've created a wrapper that returns the IOrderedQueryable that are going to allow us to work with them easily.

NHibernateSample.Tests

As its name says, there are just a bunch of tests ( I'm using XUnit as my unit test suite) that I've built to show how our ModelContext class can be used to perform simple operations as projections or to create aggregates. I reckon that the tests in there sucks. I'm in the process of learning how to write proper tests, so this is not a good example of how a set of unit tests should be written.

Let's move on and take a look at the AddressTests class, there I have the following test:

[Fact]
public override void Test_Retrieve_Entity_With_EntityID_Equals_To_X_Should_Return_Null()
{
    int addressID = 0;
    using (var context = new ModelContext(Session))
    {
        var result = (from address in context.Addresses
                      where address.AddressID == addressID
                      select address).SingleOrDefault();
        Assert.Null(result);
    }
} 

As you can see, it’s very similar to what you can do using LINQ to SQL, but with NHibernate :)
This is another test from the same class:

[Fact]
public override void Test_Sorting_Descending()
{
    using (var context = new ModelContext(Session))
    {
         var result = (from address in context.Addresses
                       where address.City == "Bothell"
                       orderby address.AddressID descending
                       select address).ToList();

        Assert.False(result.Last().AddressID > result.First().AddressID);

        foreach (var item in result)
        {
             Console.WriteLine(item.AddressID);
        }
    }
} 

Very self-explanatory. You can check the other test to have some fun with the power (and several limitations) of the LINQ provider for NHibernate.

NHibernateSample.BusinessLayer

This project is intended to be used for our web applications. I think that the interesting bits are related to the session management (NHibernateSample.BusinessLayer/SessionManagement/SessionManager.cs) and the use of StructureMap to configure our dependencies (NHibernateSample.BusinessLayer/Bootstrapper.cs, NHibernateSample.BusinessLayer/Repositories/ICustomerRepository.cs and NHibernateSample.BusinessLayer/Repositories/Implementations/CustomerRepository.cs).

NHibernateSample.CustomerWebSite

This project shows how you can use NHibernate to perform a basic CRUD operation over our Customer entity. I'll try to find the time to create a more complex example (I can't assure this :D), but with that simple aspx page you should be able to go on your own and make a nicer web app.
Some interesting things are happening in the global.asax file, and also it will help in the process to integrate ASP.NET controls with NHibernate (GridView, ObjectDataSource).

Finally, please don't forget to update your database connection in order to run the samples. The file that you need to update is located in the Solution Items folder in the solution tree, and is named hibernate.cfg.xml, you should change the value for the property connection.connection_string with one that matches your environment.

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
    <session-factory>
        <property name="current_session_context_class">
                managed_web
        </property>
        <property name="connection.connection_string">
                Data Source=YOUR_DATABASE_SERVER_GOES_HERE;
		Initial Catalog=AdventureWorks;Integrated Security=True
        </property>
    </session-factory>
</hibernate-configuration>  

Please, if you find a WTF in the source code, don't hesitate to send your feedback, I'll be very thankful.

You can download the full demo here.

Bye bye.

Shameless plug: You can check this article on my blog here.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

emiaj
Web Developer
Peru Peru
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralFew thingsmemberMike Marynowski5 Jan '10 - 11:03 
Hey,
 
I know very little about fluent NHibernate, so please excuse my potentially silly questions.
 
a) How can GetHashCode() possibly guarantee a unique result for anything that uses primary keys that use a 64-bit integer, or two 32-bit int keys, or a guid, etc.
 
b) I possibly found a WTF in your code:
 
    public class StateProvinceMapper : ClassMap<StateProvince>
    {
        private const string schema = "Person";
        public StateProvinceMapper()
        {
            SchemaIs(schema);
            Id(x => x.StateProvinceID);
            Map(x => x.Name)
                .WithLengthOf(50)
                .ReadOnly();
            References(x => x.TerritoryID)
                .LazyLoad()
                .Not.Nullable();
        }
 
Based on the class diagram you provided, there is no TerritoryID property on the StateProvice class, so where is that coming from?
 
c) I find it odd that TerritoryID is of type StateProvince and not an int. Wouldn't it make more sense to name the property just Territory? If I named it like that, how would I make the mapping work? Something like:
 
persistenceModel.Conventions.GetForeignKeyName = x => x.Name + "ID";
 
maybe?
 
-----
 
Besides that, nice intro article, thanks!
GeneralRe: Few thingsmemberemiaj6 Jan '10 - 6:45 
Hello Mike, thanks for reading the article and for giving your valuable feedback.
a) Please take into account that we are overriding the default GetHashCode with our custom implementation, which is highly oriented at how a database checks for rows equality (if they have the same primary key then they are equal). Now, to your question, I don't know if the custom implementation is 100% safe for all the cases, there're probably some edge scenarios where it may fail, thus, generating the same hashcode for entities that have different ids. I've updated the code and added some unit tests demonstrating that the GetHashCode method always returns unique results for different ids. I'll write the unit tests here but you could download the updated code to check it by yourself:
    public class HashCodeGeneratorTests
    {
        [Fact]
        public void works_for_64_bit_integer_class()
        {
            var foo = new Test64BitInt
                          {
                              Id = 1,
                              Name = "Foo"
                          };
            var bar = new Test64BitInt
                          {
                              Id = 2,
                              Name = "Bar"
                          };
 
            Assert.NotEqual(foo, bar);
 
            bar.Id = 1;
 
            Assert.Equal(foo, bar);
 
        }
 
        //More tests below
        
 
        #region Helper Classes
        public class Test64BitInt
        {
            public Int64 Id
            { get; set; }
            public string Name
            { get; set; }
 
            public override int GetHashCode()
            {
                return HashCodeGenerator.GenerateHashCode(Id);
            }
            public override bool Equals(object obj)
            {
                return this.IsEqual(obj);
            }
        }
        // More classes below

        #endregion
    }
 
 
b) Please look at the arrow at the top of the StateProvince box, you'll see it is pointing to the SalesTerritory box,thus, such property exists in the StateProvince class.
 
c) I agree with you, it seems a bit odd, furthermore, the chance you suggest is correct, by updating the GetForeignKeyName property and by changing the names removing the ID suffix, you'll get the app running without problems.
 
The updated code can be found
here.[^]
 

GeneralRe: Few thingsmemberMike Marynowski6 Jan '10 - 7:31 
a) I guess what I'm saying is that it is impossible to uniquely identify every 64 bit integer with a 32 bit integer, or to unique identify a composite key made of a pair of 32 bit integers as a single 32-bit integer. There must be overlap since you are mapping a larger set to a smaller set.
 
Caching mechanisms that use hash codes will first check hash codes and then do an equality comparison aftwards, since hash codes do not guarantee uniqueness. Hash codes by convention do not need to be unique (as this is often impossible to guarantee), they should just be evenly distributed for optimum lookup performance.
 
I'd be very surprised if this weren't the case here. The most common way to get a hash code from multiple properties is to just XOR the individual hash codes, but your method might work equally well, I'm not sure.
 
I believe that in most cases your equality function should compare actual IDs, instead of comparing generated hash codes. It's safer (no collisions in the mapping), and why generate two hash codes from the ID for comparison when you can just compare IDs?
 
b) Oops, I have no idea how I missed that massive arrow...maybe I was scrolled down so it cut off the top and I didn't see it. I looked at the diagram twice and couldn't find it, haha...I'm getting old.
GeneralRe: Few thingsmemberemiaj6 Jan '10 - 9:04 
Mike Marynowski wrote:
I believe that in most cases your equality function should compare actual IDs, instead of comparing generated hash codes. It's safer (no collisions in the mapping), and why generate two hash codes from the ID for comparison when you can just compare IDs?

 
I agree with you on this one, perhaps comparing hashes is not correct, just comparing the ids would do the trick.
One alternative method could be written as:
        public class Foo
        {
            public int Id1 { get; set; }
            public int Id2 { get; set; }
 
            public string Name { get; set; }
 

            public override bool Equals(object obj)
            {
                return this.IsEqual(obj, x => x.Id1, x => x.Id2);
            }
            public override int GetHashCode()
            {
                return HashCodeGenerator.GenerateHashCode(Id1, Id2);
            }
        }
    public static class EqualityHelper
    {
        public static bool IsEqual<T>(this T source, object obj,params Func<T, object>[] func) where T : class
        {
            var target = obj as T;
            if (target == null)
            {
                return false;
            }
            return func.All(x => x(source).Equals(x(target)));
        }
    }
 
And you can test it as follows:
            var foo1 = new Foo
                           {
                               Id1 = 1,
                               Id2 = 2,
                               Name = "X"
                           };
            var foo2 = new Foo
                           {
                               Id1 = 3,
                               Id2 = 4,
                               Name = "Y"
                           };
            Debug.Assert(!foo1.Equals(foo2));
            foo2.Id1 = 1;
            foo2.Id2 = 2;
            Debug.Assert(foo1.Equals(foo2));

 

GeneralRe: Few thingsmemberemiaj6 Jan '10 - 9:08 
Mike Marynowski wrote:
or to unique identify a composite key made of a pair of 32 bit integers as a single 32-bit integer

 
If you download the file I've uploaded and it's referred in my previous response, you'll see there are some tests in HashCodeGeneratorTests with entities that have composite ids, like...
        public class TestTwoInts
        {
            public int Id1
            { get; set; }
            public int Id2
            { get; set; }
 
            public string Name
            { get; set; }
 
            public override int GetHashCode()
            {
                return HashCodeGenerator.GenerateHashCode(Id1, Id2);
            }
            public override bool Equals(object obj)
            {
                return this.IsEqual(obj);
            }
        }
        [Fact]
        public void works_for_two_integers_class()
        {
            var foo = new TestTwoInts
            {
                Id1 = 1,
                Id2 = 2,
                Name = "Foo"
            };
            var bar = new TestTwoInts
            {
                Id1 = 3,
                Id2 = 4,
                Name = "Bar"
            };
 
            Assert.NotEqual(foo, bar);
 
            bar.Id1 = 1;
 
            Assert.NotEqual(foo, bar);
 
            bar.Id2 = 2;
 
            Assert.Equal(foo, bar);
 
            // switching ids
            bar.Id1 = 2;
            bar.Id2 = 1;
 
            Assert.NotEqual(foo, bar);
 
        }
 
I don't know if this is what are you referring to, hope this helps.
 

GeneralRe: Few thingsmemberMike Marynowski6 Jan '10 - 12:22 
Yes, I'm referring to that. In the case of composite keys, the overriden Equals function should most definitely not use the hash code for comparison, instead compare both IDs directly. If you used hash code to test equality in your TestTwoInts class, I can absolutely guarantee there are test cases where it will incorrectly think two different classes are actually equal when they arent.
 
It is physically impossible to generate a 32 bit number that can uniquely identify all possible combinations of two 32 bit numbers (effectively a 64 bit number). If that were the case, we wouldn't need 64 bit numbers, lol.
GeneralBreaks..memberbnturbanator2 Oct '09 - 21:02 
Breaks on click of Insert Customer Button
System.Data.SqlClient.SqlException: The INSERT statement conflicted with the CHECK constraint "CK_Customer_CustomerType". The conflict occurred in database "AdventureWorks", table "Sales.Customer", column 'CustomerType'.
The statement has been terminated.
Generalversion 2005memberbnturbanator1 Oct '09 - 1:24 
i dont have sql 2008, can you put together a version for vs 2005 and sql server 2005
GeneralRe: version 2005memberemiaj1 Oct '09 - 5:23 
Hi, thanks for reading the article...
About your request, I doubt it could be done, LINQ is a .Net 3.5 thing and only supported by VS2008 at this time (or VS2010 as well), and about sql 2008, the requirements says you just need sql 2005 (but it will work with sql 2008 without any problems).
 

QuestionDemo Project update please [modified]memberEM1L16 Sep '09 - 0:33 
Great Article please keep it coming! Quite a few of us mare souls can draw inspiration from embracing Fluent and Linq2NHibernate Smile | :)
 
I really liked your article and it is great to have the demo project. I'm new to Fluent NHibernate and I was looking for a sample how to implement SessionManager.
Your demo project is great but I guess due to recent API changes to the StructureMap it does not work any more and I don not have the knowledge to get it working in a few minutes. The error is in the Bootstrapper.Setup:
 
Class 'StructureMap.StructureMapConfiguration' is obsolete: "Please use the ObjectFactory.Initialise() method for configuring the container and put conguration into Registry classes"
 
I have now idea how to use ObjectFactory.Initialize(Action action);
 
Any pointers in the right direction to get your demo project working is very much appreciated!
 
Many thanks!
 
modified on Wednesday, September 16, 2009 7:50 AM

AnswerRe: Demo Project update please [modified]memberEM1L16 Sep '09 - 1:46 
I don't like the idea of replying to my own question, but certainly it will be of great help to other readers...
The answer to my question earlier is bellow. I don't know much about the StructureMap used for DI(IoC) so anyone interested should visit the project web site: http://structuremap.sourceforge.net/QuickStart.htm[^]
 
            //StructureMapConfiguration
            //    .ForRequestedType<ICustomerRepository>()
            //            .TheDefaultIsConcreteType<CustomerRepository>()
            //            .CacheBy(StructureMap.Attributes.InstanceScope.Singleton);

            ObjectFactory.Initialize(x => x
                .ForRequestedType<ICustomerRepository>()
                .CacheBy(StructureMap.Attributes.InstanceScope.Singleton)
                .AddInstances(i => i.OfConcreteType<CustomerRepository>()
                ));
 
I'm not an expert so please correct me I'm wrong...
 
thanks
 
modified on Wednesday, September 16, 2009 7:55 AM

GeneralRe: Demo Project update pleasememberemiaj16 Sep '09 - 4:52 
Hi, you are indeed correct about the StructureMap API, I'm not an expert either so I'm learning something new as well Smile | :)
There are many libraries from this article that are probably outdated, and I will need to update the article's content.
Anyway, thanks for reading it, I'll try to keep writing more about not so common things in the .net world (DI,IoC,AOP, and so on) but with simple examples.
 

GeneralGood articlememberVirat Kothari5 Sep '09 - 12:01 
I was always keen to learn about nHibernate. Your article has made me huger to learn about it. Thanks for good article..
 
Regards,
Virat Kothari

GeneralRe: Good articlememberemiaj7 Sep '09 - 4:41 
Thanks Virat, I'll try to find the time to write more articles like this.
Kind Regards,
Jaime.
 

Generalwell donememberdinolee6 May '09 - 7:07 
Smile | :)
GeneralRe: well donememberemiaj6 May '09 - 10:34 
Thank you.
 

GeneralMore pleasememberjalchr22 Apr '09 - 2:08 
Great work... could you please provide more tutorials on this subject ... that's very important
 
Use your mind for what matters most !

GeneralRe: More pleasememberemiaj22 Apr '09 - 3:29 
I'm glad you liked.
About more tutorials, I'll try to find the time to create them. Between my family and my work, my free time to do such things is very short.
However some guys out there (far more smarter than I), are constantly publishing articles relate to NH, here you can find some interesting links:
http://blogs.hibernatingrhinos.com/nhibernate/Default.aspx[^]
http://fabiomaulo.blogspot.com/[^]
http://ayende.com/Blog/[^]
Cheers.
 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 2 Jul 2009
Article Copyright 2009 by emiaj
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid