NHibernate Initial Experiments

Project Setup

To play a little bit with NHibernate and experiment with its features, I created a simple project to validate some capabilities. For now, I will be dealing with only two classes: Product and Category. I believe this will be enough for the initial experiments.

public class Product
{
    public virtual int Id { get; protected set; }
    public virtual string Name { get; set; }
    public virtual decimal Price { get; set; }
    public virtual Category Category { get; set; }
}

public class Category
{
    public virtual int Id { get; protected set; }
    public virtual string Name { get; set; }
}

I will also be using Fluent NHibernate for the mapping. It seems to be the consensum to use it, unless you really need the XML mapping provided by NHibernate. To install everything it is just a matter of installing one NuGet Package.

Mapping and Session Factory

Here is the mapping classes that I’m using:

public class ProductMap : ClassMap<Product>
{
    public ProductMap()
    {
        Id(x => x.Id, "product_id");
        Map(x => x.Name, "name").Not.Nullable();
        Map(x => x.Price, "price")
            .Not.Nullable().CustomSqlType("money");
        References(x => x.Category, "category_id");
    }
}

public class CategoryMap : ClassMap&amp;lt;Category&amp;gt;
{
    public CategoryMap()
    {
        Id(x => x.Id, "category_id");
        Map(x => x.Name, "name").Not.Nullable();
    }
}

And here is the SessionFactory class:

public class SessionFactory
{
    private static ISessionFactory _factory;

    public static void Init(string connectionString) {
        _factory = BuildSessionFactory(connectionString);
    }

    public static ISession OpenSession() {
        return _factory.OpenSession();
    }

    private static ISessionFactory BuildSessionFactory(string cs) {
        FluentConfiguration configuration = Fluently.Configure()
            .Database(MsSqlConfiguration
                .MsSql2012
                .ConnectionString(cs)
                .ShowSql()
            )
            .Mappings(m =&amp;gt; m
                .FluentMappings
                .AddFromAssembly(typeof(SessionFactory).Assembly));

        return configuration.BuildSessionFactory();
    }
}

The .ShowSql() method will display the generated SQL in the console window. This is very helpful. I found solutions on the internet that suggests to use log4net, but I didn’t find it necessary for what I’m doing right now.

Public Properties as Virtual

NHibernate was designed to work with POCO objects and it is also fully optimized to work with proxies that requires your classes to be not sealed and have all public properties as virtual. You can find more information about the requirements here.

By working with proxies, you can take advantage of Lazy Loading and access the ID from the Category property without hitting the database, because the product table already has the category ID. NHibernate will try to load the category from the database only if we try to access other properties, like name.

var product = session.Get<Product>(1);
Console.WriteLine("Category ID is {0}", product.Category.Id);

I found this great because you don’t have to keep a property to store the reference id. Only the object reference will be enough.

public class Product
{
    public virtual int Id { get; protected set; }
    public virtual string Name { get; set; }
    public virtual Category Category { get; set; }
}

Difference Between Get and Load

You not only can read the Category ID using the navigation property but also set it without having to hit the database to create a category object.

using (var session = SessionFactory.OpenSession())
{
  using (var transaction = session.BeginTransaction())
  {
    var product = session.Get<Product>(1);
    product.Category = session.Load<Category>(2);

    transaction.Commit();
  }
}
  • Line 5: will query the database for the product right away.
  • Line 6: will not query the database for the category.

When you get an entity using Load, it will only hit the database when you actually access a property for that object, for example:

using (var session = SessionFactory.OpenSession())
{
  using (var transaction = session.BeginTransaction())
  {
    var product = session.Load<Product>(1);
    Console.WriteLine(product.Name);

    transaction.Commit();
  }
}
  • Line 5: will not access the database to create the object.
  • Line 6: will access the database to fill the object properties.

At this point you can probably think that it is better to use Load instead of Get, but there is another major difference that will help with the decision of when to use one or the other. The Get method will return null if the ID is not found and will not throw any exception, the Load method will return a proxy of the object, but it will throw an exception only if you try to access a property from this proxy and the ID does not exist in the database.

Method Throw Exception Create Proxy
Get False False
Load True Yes

My understanding is that, if we don’t know if the ID exists in the database, we should use the Get method and check if the result is null. If we know for sure that the ID exists in the database and we don’t need to access any property from it, we should use the Load method instead.

Conclusion

So far, I’m very happy with the results. Still, there’s a lot of tests that I would like to do, like: inheritance, many-to-many mapping and working with UUID.

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