Oct 08 2009

NerdDinner with Fluent NHibernate Part 3 – The infrastructure

Category: Uncategorizedbengtbe @ 05:00

This is the final post in a series of three where I’m going to see how we can change the NerdDinner project to use Fluent NHibernate instead of LINQ to SQL:

Introduction

In the first post we took a look at the domain model of NerdDinner.com, and recreated and improved the code that was auto generated by the LINQ to SQL designer. In the second post we saw how to map this domain model to the database using Fluent NHibernate. In this final post we are going to take a look at the required changes to the rest of the application.

Session per request

In order for the repository to use NHibernate it needs to get a hold of an ISession. In this application I have chosen to follow the session per request pattern, in which a session is created at the beginning of the HTTP request, and closed at the end of the HTTP request. I also wanted to keep the changes to the NerdDinner project as small as possible; hence the entire infrastructure for NHibernate is isolated in a single class:

public class NHibernateSessionPerRequest : IHttpModule

{

    private static readonly ISessionFactory _sessionFactory;

    static NHibernateSessionPerRequest()

    {

        _sessionFactory = CreateSessionFactory();

    }

    public void Init(HttpApplication context)

    {

        context.BeginRequest += BeginRequest;

        context.EndRequest += EndRequest;

    }

    public static ISession GetCurrentSession()

    {

        return _sessionFactory.GetCurrentSession();

    }

    public void Dispose() { }

    private static void BeginRequest(object sender, EventArgs e)

    {

        ISession session = _sessionFactory.OpenSession();

        session.BeginTransaction();

        CurrentSessionContext.Bind(session);

    }

    private static void EndRequest(object sender, EventArgs e)

    {

        ISession session = CurrentSessionContext.Unbind(_sessionFactory);

        if (session == null) return;

        try

        {

            session.Transaction.Commit();

        }

        catch (Exception)

        {

            session.Transaction.Rollback();

        }

        finally

        {

            session.Close();

            session.Dispose();

        }

    }

    private static ISessionFactory CreateSessionFactory()

    {

        string connString = "NerdDinnerConnectionString";

        FluentConfiguration configuration = Fluently.Configure()

        .Database(MsSqlConfiguration.MsSql2005.ConnectionString(

           x => x.FromConnectionStringWithKey(connString)))

        .ExposeConfiguration(

            c => c.SetProperty("current_session_context_class", "web"))

        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<Dinner>());

        return configuration.BuildSessionFactory();

    }

}


Some comments about the infrastructure:

  • It implements the IHttpModule, in order to plug into the request handling pipeline.
  • Since it is expensive to create an ISessionFactory you only want to create this once. This is why the factory is stored in a static field that is initialized by the static constructor.
  • At the beginning of the HTTP request the ISession is created and “stored” using the CurrentSessionContext.Bind method.
  • Other code can get a hold of this session by calling the GetCurrentSession method.
  • At the end of the HTTP request we “remove” the session by using the CurrentSessionContext.Unbind method. We also commit any changes, and close the session.

There are some drawbacks to this approach. We create a session for every request, even if it is not needed. However, a session is very lightweight, and we can easily tweak this behavior later. Another drawback is that we commit the transaction at the end of the HTTP request. In a more complex business application you probably want more control over your unit of work.

In order to activate this IHttpModule we need to add the following to httpModules/modules section of the Web.config:

<add name=”NHibernateSessionPerRequesttype=”NerdDinner.Models.NHibernateSessionPerRequest” />

Changes to the repository

In the DinnerRepository we are mainly going to use LINQ to NHibernate to replace the LINQ to SQL queries:

public class DinnerRepository : IDinnerRepository

{

    public ISession Session

    {

        get { return NHibernateSessionPerRequest.GetCurrentSession(); }

    }

    public IQueryable<Dinner> FindAllDinners()

    {

        return Session.Linq<Dinner>();

    }

    public IQueryable<Dinner> FindUpcomingDinners()

    {

        return from dinner in FindAllDinners()

               where dinner.EventDate >= DateTime.Now

               orderby dinner.EventDate

               select dinner;

    }

    public IQueryable<Dinner> FindByLocation(float latitude, float longitude)

    {

        return Session.CreateSQLQuery(

                @”SELECT d.*

                    FROM Dinners d

                    JOIN NearestDinners(:Latitude, :Longitude) n

                      ON d.DinnerId = n.DinnerId

                   WHERE EventDate >= :EventDate

                   ORDER BY EventDate Desc”)

            .AddEntity(typeof(Dinner))

            .SetDouble(“Latitude”, latitude)

            .SetDouble(“Longitude”, longitude)

            .SetDateTime(“EventDate”, DateTime.Now)

            .List<Dinner>().AsQueryable();

    }

    public Dinner GetDinner(int id)

    {

        return Session.Linq<Dinner>()

            .SingleOrDefault(d => d.DinnerID == id);

    }

    public void Add(Dinner dinner)

    {

        Session.SaveOrUpdate(dinner);

    }

    public void Delete(Dinner dinner)

    {

        Session.Delete(dinner);

    }

}

The LINQ to SQL and NHibernate Linq syntax are similar, so the changes to this class were fairly simple. However in the FindByLocation method I had to create a custom SQL query, because I don’t think (not sure) that LINQ to NHibernate supports calling the database function NearestDinners.

Update: It seems that NHibernate Linq supports database functions. However I couldn’t find much information about the functionality except for one simple example in the source code.

The ISession is retrieved by calling the GetCurrentSession() method. In an application with an IoC container I would prefer to inject the session in the constructor.

finally {}

In the final post in this series we have taken a look at the necessary changes to the infrastructure. We used a session per request pattern to create the session, and LINQ to NHibernate in the repository. As you hopefully have seen it was not very difficult to replace LINQ to SQL with Fluent NHibernate. As a benefit the domain part of the application has been greatly improved over the auto generated code from the LINQ to SQL designer.

If you liked this post then please shout and kick me :)

kick it on DotNetKicks.com Shout it

Tags: , , ,

16 Responses to “NerdDinner with Fluent NHibernate Part 3 – The infrastructure”

  1. Fabio Maulo says:

    You should inject the ISessionFactory to your Repository instead have a reference to the HttpModule

  2. Bengt Berge says:

    Hi Fabio. I totally agree that it would be much better to inject the ISessionFactory or ISession in the constructor, and I sort of mentioned this in the post:

    [quote]In an application with an IoC container I would prefer to inject the session in the constructor. [/quote]

    However I wanted to keep the changes to the NerdDinner project as small as possible. If I was going to write a part 4 in this series it would certainly be about introducing an IoC container :)

  3. antimonio says:

    IMO NHibernate is a huge project with a lot of possibilities that lets you migrate normal SQL software products to ORM. But the philosophy behind Fluent NHibernate is to keep it simple, and using functions/storedprocedures of the database goes against this rule. You should drop them and convert them to real NHibernate Query code (HQL, Criteria…) to consider it a real Fluent NHibernate use case.

  4. Alan Christensen says:

    Great series but this long awaited third part is not quite what I was expecting.
    I’m not sure it is equivalent to the Linq2Sql since we this has raw DB SQL and IHttpModule.

    I would love to see a version of part three using an IoC container instead of the IHttpModule and NHibernateLinq for FindByLocation().

  5. Bengt Berge says:

    Hi antimonio. Thanks for your comment!

    This post series are an example of introducing NHibernate to an existing project. In these cases one of the main strengths of NHibernate is that it is very flexible and that it even allows you to write some queries in SQL. This allows you to convert the project incrementally.

    Also if you take a look at the DistanceBetween algorithm of NerdDinner, you will see that it is actually quite complex, and not easily translated to either HQL or Criteria. You can see the algorithm here:

    http://nerddinnerbook.s3.amazonaws.com/Part11.htm

    In this specific case I also think that a SQL function will give the best performance.

  6. Bengt Berge says:

    Hi Alan. Thanks for your comment!

    I understand that you wanted to see an IoC container in the project. However my goal for this series was to replace Linq to SQL with Fluent NHibernate and still keeping it simple.

    If I also introduced an IoC container some readers might get the wrong impression that you also need to understand IoC/DI in order to use Fluent NHibernate.

    Another reason for not talking about IoC container in this post, is that I didn’t want to repeat myself. I have already written a post on how to use StructureMap with ASP.NET MVC:

    http://www.bengtbe.com/blog/post/2009/02/27/Using-StructureMap-with-the-ASPNET-MVC-framework.aspx

    If you want to read more about using NHibernate with an IoC container you should check out this recent post by Weston Binford:

    http://trason.net/journal/2009/10/7/bootstrapping-nhibernate-with-structuremap.html

  7. adolfojp says:

    Is there a download link for the code of the tutorial? This is a nice resource. I would love to be able to dissect it myself.

  8. Bengt Berge says:

    Hi adolfojp. I have not offered the code as a download. Mostly because I’m not sure if it is allowed according to the NerdDinner license (and because I then would have to clean up the solution some more).

    You can of course download the original NerdDinner code, and try to implement Fluent NHibernate based on my posts. The original code can be found at: http://nerddinner.codeplex.com/

  9. mgroves says:

    Anyone know why I would get an exception "No current session context configured" when using CurrentSessionContext.Bind(session)?

  10. Ondrej Stastny says:

    mgroves: You have to configure NHibernate with parameter current_session_context_class = thread_static.

    This is how you would do it in Fluent NHibernate:
    ExposeConfiguration(c => c.SetProperty("current_session_context_class", "thread_static"))

    Regards,

    Ondrej

  11. mgroves says:

    Actually, I set the current_session_context_class property to "web", per an email from Bengt. That parameter is still somewhat black magic to me at this point, so I don’t know if the property you suggested would work or not, and specifically why it would work or not.

  12. bengtbe says:

    Hi mgroves.

    Setting the "current_session_context_class" to "web" causes NHibernate to store the current session in the HttpContext when you call the CurrentSessionContext.Bind method. It can then later be retrieved from the HttpContext by calling the Unbind method. The HttpContext is a per-request cache that is very suitable to use to store the session for a request.

    Bengt

  13. developingchris says:

    This is non-sequitor, but what do you use for posting code in your blog with that nice format?

    Nice post, thanks for concrete examples.

  14. Bengt Berge says:

    Hi Chris. Thanks for your comment!

    The format of the code is identical to my Visual Studio theme. To post the code on my blog I then use a Visual Studio plug-in called CopySourceAsHtml.

  15. developingchris says:

    Now I have a sequitor comment. Thanks for the plugin suggestion. So I understand the current_session_context_class being web for web, but what should it be for the test runner? I still want to use a similar configuration in test when I’m in integration test mode.

    I’m implementing nhibernate in an app that has many other ormy things going on, so I really have to do almost full stack testing, just below the ui layer to the metal and just make sure its working, working my way down the layers into code coverage nirvana.

    Am I just in the place where I require a unit of work implementation to really run? I have run into transaction timing issues with the standard Rhino commons implementation, reimplemented by gabriel as well, both just act funny and require a lot of transaction start code in my tests. What to do?

  16. Bengt Berge says:

    Hi again Chris,

    It is difficult to give a good answer to all your questions here in the comments. It is correct that the current_session_context_class being web is used for web, so it would not work (I believe) for the tests.

    I usually manage the transaction and session in the test themselves, usually in a base class which all my integration test inherit from. In the repository class you then use dependency injection to inject ISession in the constructor. This means that you need some extra code to handle the transaction in the tests, but most of this can be in the base class.

    Take a look at the following post by Davy Brion an example:
    http://davybrion.com/blog/2009/12/unit-testing-an-nhibernate-application/

    I’m not to familiar with Rhino Commons, so I can’t comment on that.

Leave a Reply