Setting up ASP.NET MVC with Fluent NHibernate and StructureMap

On a yet-to-be-released side project of mine, I decided to use Fluent NHibernate, StructureMap, and ASP.NET MVC. It took me awhile to get everything to play together nicely, so I documented the steps I took in case anyone out there was interested in using in a similar setup.

Step 1: Set up StructureMap

First, I created the StructureMapControllerFactory class (taken from the MvcContrib project):

I then added the following line to the Application_Start() function in Global.asax.cs so that StructureMap would inject dependencies whenever an ASP.NET MVC controller was instantiated:

Step 2: Set up Fluent NHibernate

Next, I created a static function for creating an ISessionFactory (this code may look very different depending on your database and project setup):

Step 3: Hook up Fluent NHibernate with StructureMap

Then, I updated my Application_Start() function in Global.asax.cs so that StructureMap would be aware of how to instantiate an NHibernate ISessionFactory and ISession:

Now, whenever StructureMap needs to create an ISessionFactory, it calls the CreateSessionFactory() method defined in Step 2. Since ISessionFactory is expensive to create, I have configured StructureMap to create it as a Singleton so that it will only be created once per application.

Similarly, whenever StructureMap needs to create an ISession, it will create/retrieve an ISessionFactory instance and call its OpenSession() method. This is scoped at the HttpContext level, which means that at most, one ISession will be created by StructureMap per web request.

Step 4: Clean up

Finally, in order to ensure that we aren’t leaking ISessions on every web request, I added the following line to my Application_EndRequest function to properly dispose of the ISession StructureMap may have created during that web request:

The setup described above works on my machine with the following versions:

  • ASP.NET MVC 2
  • StructureMap 2.6.1
  • Fluent NHibernate 1.0.0.594
 

Kevin Pang

Kevin is a software engineer at Google whose programming interests revolve around web development, software architecture, and design. When he's not writing software, he enjoys watching Jeopardy!, playing Magic the Gathering, and wandering around Disneyland.

 

26 thoughts on “Setting up ASP.NET MVC with Fluent NHibernate and StructureMap

  1. Isn’t that code still leaking ISessions? I mean, the sessions are only being cleaned up at the end of the application but the cleanup should happen at the end of each request…

    1. No, Application_EndRequest is fired at the end of every web request. I think you’re confusing it with the Application_End method, which is fired at the end of the application.

  2. Kevin, Great post. I’ve been looking for a more recent post about StructureMap. Many of the posts that I’ve found on the web are a few years old, and although are still applicable, many use the deprecated ForRequestedType. I’ve been trying to find some more recent posts as well that talks about some of the more advanced features of StructureMap. This one hit home for me and a project I’m developing at work.

    Thanks again,
    Jefferson

  3. I’ve wanted to move away from another IoC provider and migrate to StructMap for some time now. I thought I should let you know your well written yet simple to follow post convinced to make the change tonight.

    Thanks so much for documenting this Kevin!

    1. Require an ISession as a parameter in your controller’s constructor and StructureMap (if it’s been set up correctly) will automatically inject it for you.

      For instance:

      private ISession session;

      public MyController(ISession session)
      {
      this.session = session;
      }

      1. I got an error “No parameterless constructor defined for this object” when I create my controller’s constructor with the parameter ISession. Anything wrong? Also, where do you specify the assembly file of say the IRepository? In the web.config or global.asax? Can you email me the set up in web.config or global.asax for the structuremap part?

        Thanks ton!

  4. I like how you have this setup, except that I’m not injecting it into my controllers, I’m injecting it into my repository. And by the time the ISession gets to my repository, it’s closed. I don’t want to ahve to setup every controller to have an ISession that pass down to the repository.

    Thoughts?

  5. Well, nevermind. Just after i posted the comment i figured one piece out. I hvae my repository setup as a singleton in my mvc app by structuremap. so of course, when it comes around to the calling of a method, the injected ISession has been closed and disposed.

    ForSingletonOf().Use(x => x.GetInstance());
    .......
    private ISession Session;
    public CustomerRepository(ISession session) {
    Session = session;
    }
    public Customer GetCustomer(int CustomerNumber) {
    var customer = Session.Linq().FirstOrDefault(x => x.CustomerNumber == CustomerNumber);


    So How do i get the session back into the repository without having to put a parameter on each method? Or is that what needs to be done?

    1. @Mike: Either don’t scope your repositories, or scope them to the HttpContext. Then, their contructors will get the necessary ISession just fine. I see no reason to scope repositories as singletons.

  6. How have you set up unit tests for this system? If the data access layer has the NHibernate calls, but the session is created in the httpcontext, how do you create unit tests for the data access layer? I assume you would inject the session in the data access layer constructors, but it seems like you’d need to move the session creation out of the ObjectFactory.Initialize and put it in each TestSetup?

    1. You’re right in that if you want to unit test your data access layer using this setup, your unit tests will have to be responsible for NHibernate session management.. What I’ve done in my project is create a DatabaseTest base class that handles this in each TestSetup. It’s a little bit of extra work and code duplication, but it hasn’t bothered me so far.

      For unit tests on classes that utilize the data access layer, you won’t want to be connecting to a live database anyways so you can simply stub/mock out your repositories and not have to worry about creating NHibernate sessions.

      1. Can you post the code for your DatabaseTest base class and how it’s used in your TestSetup/TestFixtureSetup? We have an environment where our legacy database has a lot of business logic in triggers and so on, so we need integration tests that actually hit the database, so I would love to see how you handle database integration testing.

        1. Here’s my DatabaseTest class:

          public class DatabaseTest
          {
          protected ISessionFactory sessionFactory;
          protected ISession session;

          public DatabaseTest()
          {
          sessionFactory = CreateSessionFactory();
          }

          public void SetUp()
          {
          session = sessionFactory.OpenSession();
          }

          public void TearDown()
          {
          if (session != null && session.IsOpen)
          session.Close();
          }
          }

          I have to manually call SetUp() and TearDown() from the setup/teardown routines in any classes that inherit from this (I ran into a problem using the SetUp and TearDown attributes in DatabaseTest because NUnit seemed to not call them if I also had functions in the child class with SetUp and TearDown attributes).

          This ensures that before each test is run I have a clean ISession to use and that it’s properly disposed of after each test is run.

          1. Here’s a sample unit test using the DatabaseTest class:

            public class UserRepositoryTests : DatabaseTest
            {
            [Test]
            public void Test_Get_By_User_Id()
            {
            var userRepository = new UserRepository(session);

            var user = userRepository.GetByUserId(1);

            Assert.IsNotNull(user);
            }
            }

    1. You put your connection string name where “InsertConnectionString” is in my code example. The connection string should be defined in a .config file (e.g. web.config or app.config).

Comments are closed.