;

Persistence Ignorant POCOS part 3 – NHibernate

Posted : Friday, 18 March 2011 15:07:29

In this, the third post in a series comparing the developer experience of hooking up a POCO using various ORMs I shall be creating an NHibernate implementation of the application. NHibernate is an open source project, it is a port to .Net from the highly popular JAVA persistence tool Hibernate. In order to speed things up a bit (as well as making life about a million % easier) I will be using fluent NHibernate which allows for a config-file free configuration of NHibernate (under the covers it does exactly the same but you don’t need to write masses of xml).

This first step in creating the NHibernate ‘flavour’ of the persistence layer was to add a new class library to the application called LcaModel.NHibernate. To this class library project I added an implementation of each of the repositories and a class to handle the wiring up of the dependencies. The project structure now looked like this:

poco3-solution

I also had to add a few references to the project that NHibernate requires – I wont list all of these but they are contained in the _Assemblies folder of the accompanying download.

As you can see the naming convention is pretty straightforward. The implementation of each of the repositories was created using the Visual Studio ‘implement interface’ context menu functionality so all methods would throw a NotImplementedException if invoked. The NHibernateModule class shown above simply tells Ninject to use the NHibernate implementations of each of the repositories (see part 1 for a refresher of how the DI part of the application works). In order to actually use this new implementation I simply altered the command line parameter of the LightsCameraAuction project to “NHibernate” and hit ‘F5’. As soon as the application launched an exception was thrown, not the NotImplementedException I was expecting though. I altered the error handler in the Main method of the LightsCameraAuction.Program class to feedback the error test to the console window and ran it again, this is what I got:

referencebug

Its fairly clear what’s causing the error but it took me a while to solve the problem. What was a little odd was that the I had added the NHibernate.ByteCode.Castle as a reference to the project but it was not copying to the bin directory. I should also note here that in previous posts I was using a post build event to make sure the persistence assembly (in this case LightsCameraAuction.NHibernate.dll) was located in the bin directory of the LightsCameraAuction project but for this implementation I simply added this NHibernate project as a reference to the LightsCameraAuction project meaning that on building, this assembly (and all its dependencies) would should be copied to where the LightsCameraAuction.exe could find it. The reason that the NHibernate.ByteCode.Castle.dll was not being copied was down to the fact that none of the code in my new project was directly using it. I found this out (and the subsequent fix) thanks to this post. The fix was to add another class to this project, the contents of which are shown below:

    5     internal class ReferenceBug

    6     {

    7         /// <summary>

    8         /// https://forum.hibernate.org/viewtopic.php?f=25&t=1007657&start=0

    9         /// </summary>

   10         static void Fix()

   11         {

   12             Castle.DynamicProxy.DefaultProxyBuilder fix1 = new Castle.DynamicProxy.DefaultProxyBuilder();

   13             ProxyFactoryFactory fix2 = new ProxyFactoryFactory();

   14         }

   15     }

This basically forces visual studio to copy the NHibernate.ByteCode.Castle.dll along with all the other assemblies.

So after adding this fix I ran the project again and this time got the NotImplementedException I was expecting as soon as the AuctionService attempted to add a Bidder to the NHibernateBidderRepository. So the next step was to add the persistence code. Nhibernate operates using an object known as a Session, this is the means by which entities are change-tracked, lazy-loaded and persisted – it works in a similar way as the ObjectContext does in EntityFramework. In order to utilise this Session class, an instanmce needs to be created – this is achieved by means of a factory class known as a SessionFactory which is what contains all of the configuration data required to connect to the database. Given that I would be using the same SessionFactory for this project I created a class class NHibernateSessionFactory and added it as a constructor parameter to each of the repository implementations which would now hold a private member variable reference to it. Finally I wired up Ninject to use this class with a singleton lifecycle. The complete listing of the NHibernateModule is shown below:

    1 using System;

    2 using System.Configuration;

    3 using LcaModel.Interfaces;

    4 

    5 namespace LcaModel.NHibernate

    6 {

    7     public class NHibernateModule : LcaModule

    8     {

    9         public override string MetadataBindingParamValue

   10         {

   11             get

   12             {

   13                 return "NHibernate";

   14             }

   15         }

   16 

   17         public override void Load()

   18         {

   19 

   20             //check there is a connection string

   21             var connectionStringSection = ConfigurationManager.ConnectionStrings["LightsCameraAuction"];

   22             if (connectionStringSection == null || string.IsNullOrEmpty(connectionStringSection.ConnectionString))

   23                 throw new ApplicationException("no connection string defined");

   24 

   25             Bind<NHibernateSessionFactory>()

   26                 .ToSelf()

   27                 .InSingletonScope()

   28                 .WithConstructorArgument("connectionString", connectionStringSection.ConnectionString);

   29 

   30             Bind<AuctionService>()

   31                 .ToSelf()

   32                 .WithMetadata(MetadataBindingParamName, MetadataBindingParamValue);

   33 

   34             Bind<IItemRepository>()

   35                 .To<NHibernateItemRepository>()

   36                 .When(c => c.ParentContext.Binding.Metadata.Get(MetadataBindingParamName, "") == MetadataBindingParamValue);

   37 

   38             Bind<IBidRepository>()

   39                 .To<NHibernateBidRepository>()

   40                 .When(c => c.ParentContext.Binding.Metadata.Get(MetadataBindingParamName, "") == MetadataBindingParamValue);

   41 

   42             Bind<IBidderRepository>()

   43                 .To<NHibernateBidderRepository>()

   44                 .When(c => c.ParentContext.Binding.Metadata.Get(MetadataBindingParamName, "") == MetadataBindingParamValue);

   45 

   46             Bind<IAuctionRepository>()

   47                 .To<NHibernateAuctionRepository>()

   48                 .When(c => c.ParentContext.Binding.Metadata.Get(MetadataBindingParamName, "") == MetadataBindingParamValue);

   49         }

   50     }

   51 }

   52 

I should also add that given that the LightsCameraAuction.EntityFramework required non-standard format connection string I had to add another connection string to the app.config file of the LightsCameraAuction project. Now I could add the persistence code to the repositories. I started with NhibernateBidderRepository and have detailed the Add() method below:

   16         public void Add(Bidder bidder)

   17         {

   18             using (var session = _sessionFactory.OpenSession())

   19             {

   20                 session.Save(bidder);

   21             }

   22         }

As you can see its pretty simple. After implementing the rest of NhibernateBidderRepository. I ran the project again, this time I hit the following error:

unmappedclass_4

This is pretty much what I’d expect seeing as NHibernate had no idea of how to persist the Bidder class…yet…..

Mapping classes using fluent NHibernate is a breeze and there is plenty of documentation on the web. Admittedly Bidder is a very trivial class but the mapping for it is almost as trivial:

    5     public class BidderMap : ClassMap<Bidder>

    6     {

    7         public BidderMap()

    8         {

    9             Id(x => x.BidderId).Column("bidder_id");

   10             Map(x => x.Name).Column("bidder_name");

   11         }

   12     }

In order to ‘tell’ NHibernate to use this mapping class we simply provide the assembly containing the mapping data to the configuration code in the NHibernateSessionFactory class.

   11         public NHibernateSessionFactory(string connectionString)

   12         {

   13             _sessionFactory = Fluently.Configure()

   14                 .Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)

   15                 )

   16                 .Mappings(m => m.FluentMappings

   17                                    .AddFromAssemblyOf<NHibernateBidderRepository>()

   18                                    .Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Never())

   19                 )

   20                 .BuildSessionFactory();

   21         }

   22 

You can also see Lazy-Loading is turned off - as stated in the beginning of this series, Lazy-Loading won’t be used in any of the implementations.

After running the program again, the code got a little further as hoped but now complained because the other classes had not yet been mapped. In order to check it was working I had a look in the bidder table and there indeed were three records – Fred, Barney and Wilma:

bidders

So I pressed on and completed the implementations on the other repositories, mapped the remaining classes and reran the application:

sucess

So all good – no changes to the application itself or the model and the LightsCameraAuction application was now using NHibernate as its persistence mechanism.

On the whole the process of switching the application to use NHibernate was a breeze and using the fluent interface was so much easier that the whole xml mapping process of using EntityFramework in post 2. The only minor hack was the reference bug mentioned earlier but given that the fix was an internal class the goal of persistence ignorance was still achieved (and relatively easily so). So, next I took a step back in time (and some may say technology) to the world of LinqToSql…

  • (This will not appear on the site)