Castle Windsor and nHibernate in ASP.NET MVC

Years later after this post was originally written, things have improved – see updates.

I will in this post attempt to show how to make a simple DI scheme, access data through the nHibernate O/R-mapper, and present it in an ASP.NET MVC web site. I will present sample code and sample XML along the way and I have also bundled the Visual Studio solution and sample database in an easy-to-download Zip file. Work was greatly simplified by excellent sources, that are listed below

The Premise

The web site in this case is rudimentary to the power of three. It will display one list. On the start page. No clicking involved, just straight to the point.

The list will contain services and items. The point is to illustrate a need to uniformly treat information from two sources. In this case there are two tables in the same database, but conceptually you could have one piece of information in the ERP system and the other one in the CRM system, for instance.

The Domain Classes

The two types of data, Items and Services, are implemented in the POCO classes Item and Service. Both inheriting common properties from the POCO class Entity for the sake of me not having to write as much code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataModel
{
    public class Entity
    {
        public virtual int Id { get; set; }
        public virtual string Description { get; set; }
        public virtual string Identifier { get; set; }
        public virtual double UnitPrice { get; set; }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataModel
{
    public class Item : Entity
    {
        public virtual double Inventory { get; set; }

        public Item() {
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataModel
{
    public class Service : Entity
    {
        public virtual double ExpectedDuration { get; set; }

        public Service() { 
        }
    }
}

As you can tell, the classes exposed to nHibernate  require a public default constructor. Properties must be public and virtual.

In SQL Server two tables are created, Item, Service and the common fields are inherited through Copy/Paste.

The Mapping

For this exercise I went with nHibernate 2.0.1. To use that at the same time with Castle Windsor you need to directly specify the NHibernate assembly of the correct version and avoid the 1.2 version shipped with Castle Project. To do this I simply chose the Browse tab under Add Reference… and pointed to the correct assembly. Be careful here to double check your assembly versions, because if there was a blooper reel to this blog you would have seen a clip of me trying to configure a 1.2 nHibernate assembly with 2.0 XML because I had taken the NHibernate reference at face value. <Canned laughter>

For each domain table I made a mapping file to map the assembly, namespace and class with the physical table and the individual fields with their corresponding properties. Very straightforward indeed. The files are named in the following manner: namespace.class.hbm.xml As you can plainly see, the properties inherited from the Entity class are mapped as if they belonged to the Item or Service classes directly. Below: DataModel.Item.hbm.xml

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="DataModel"
                   assembly="DataModel">

  <class name="Item" table="Item">

    <id name="Id" column="Id" type="Int32">
      <generator class="identity"/>
    </id>

    <property name="Description"    column="Description"    type="String"/>
    <property name="Identifier"    column="Identifier"    type="String"/>
    <property name="UnitPrice"    column="UnitPrice"    type="Double"/>
    <property name="Inventory"    column="Inventory"    type="Double"/>

  </class>

  <query name="DataModel.Item.GetAll">
    <![CDATA[
    from DataModel.Item
  ]]>
  </query>

</hibernate-mapping>

 

In order to fetch a list of Items, the XML file contains a very simple query definition that simply requests every record from the table sans filter. As we take a look at the file containing the mapping against the Service table, we see very similar content:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   namespace="DataModel"
                   assembly="DataModel">

  <class name="Service" table="Service">

    <id name="Id" column="Id" type="Int32">
      <generator class="identity"/>
    </id>

    <property name="Description"    column="Description"    type="String"/>
    <property name="Identifier"    column="Identifier"    type="String"/>
    <property name="UnitPrice"    column="UnitPrice"    type="Double"/>
    <property name="ExpectedDuration"    column="ExpectedDuration"    type="Double"/>

  </class>
  <query name="DataModel.Service.GetAll">
    <![CDATA[
    from DataModel.Service
  ]]>
  </query>


</hibernate-mapping>

 

NHibernate requires Session state to be maintained. This is performed through the use of an Titmouse that maintains the connection across an entire IIS request. You could figure this code out on your own or follow my lead and find it online. The HttpModule is registered with the IIS  web.config and is thereafter managed automagically by the web server. A caveat I ran into was that you cannot just register modules willy nilly on a Vista machine, You have to command some authority over the applicationHost.config file and specifically allow the server or, rather, individual web sites to add modules on their own.

I have included an NHibernateHelper class with static methods to execute the queries that we defined in the XML files above. The helper class is quite accomplished in that it has built in paging and is very clean and easy to understand. It was written and conceived by a colleague of mine.

At this point we have:

  1. Created POCO classes that represent our domain information
  2. Created SQL Server tables that contain the information we wish to display
  3. Created mapping files to correlate between 1 and 2 and define queries for future use
  4. Set up Session state in NHibernate through the use of the IHttpModule interface
  5. Defeated the applicationHost.config issue
  6. Included helper functions to help us execute the queries defined in 3.

The IoC scenario

To illustrate Inversion of Control I chose a very simple example. The web site wants to display a list of stuff that you can buy, regardless of whether it’s goods or services, whether it’s stored in the database directly or catalogued in an ERP system somewhere.  In this extremely simple example, the Controller will itself call the data retrieval function and collect all data in a List<Entity>.

A more feature rich option would have been to create a class for the data retrieval function below and register it as a Castle component and then let Castle create the data retrieval function with the implementations for Services or Items already loaded. I may just do that in the future.

How is all this implemented you ask?

In the web.config file, two Castle.Windsor Components have been defined  Both of them return objects of the Entity class. One retrieves Items and the other retrieves Services. 

 <castle>
    <facilities>
      <facility id="loggingfacility" type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging" loggingApi="log4net" configFile="logging.config" />
    </facilities>
    <components>
      <component id="ProductData" type="DataSourceFacility.ProductInfoFactory, DataSourceFacility" />
      <component id="ServiceData" type="DataSourceFacility.ServiceInfoFactory, DataSourceFacility" />
    </components> 
  </castle>

In the Home controller in the website, both components are queried and the results go in a List<Entity> that is passed through to the HTML View and there indeed presented to the user.

As is plainly visible, the component names are daringly hard coded in the call to LoadData below.

        public ActionResult Index()
        {
            ViewData["Title"] = "Home Page";
            ViewData["Message"] = "Welcome to ASP.NET MVC!";

            List<Entity> l = new List<Entity> ();

            LoadData(ref l, @"ProductData");
            LoadData(ref l, @"ServiceData");

            ViewData["Collection"] = l;

            return View();
        }
            WindsorContainer container = new WindsorContainer(new XmlInterpreter());

        private void LoadData(ref List<Entity> lst, string provider)
        {
            DataSourceFacility.IDataFactory source = container.Resolve<DataSourceFacility.IDataFactory>(provider);
            lst.AddRange(source.GetData(NHibernateModule.CurrentSession));
        }

 

Where are we now?

  1. We have defined data access classes in a dedicated assembly. 
  2. These data access classes are registered as components in web.config
  3. The Castle Windsor dependency injection container thereafter activates the classes by name
  4. Data is collected from the two sources into one list ready to be displayed to the user.
The GUI

In closing I just want to mention the issues that came from going from Preview 3 to Beta on the ASP.NET MVC framework while writing this post. The HtmlHelper class no longer has an ActionLink so I had to review my Master Page and change to an Ajax.ActionLink.Of course, the RenderPartial function was not available anymore either so the login box had to go.

After all this, 5 minutes after I close Visual Studio for the last time, Scott Guthrie reveals the first release candidate and you’ll be in for a new set of challenges porting this website to the RC1 ASP.NET MVC framework.

The conclusion

The Castle Windsor dependency injection container and NHibernate work just fine together as long as the version issues are kept at bay. NHibernate deals well with inheritance, but the ‘all properties must be virtual’ and ‘there must be a public default constructor’-bit destroys the purity of the O/R concept.

5 thoughts on “Castle Windsor and nHibernate in ASP.NET MVC

Leave a Reply

Your email address will not be published. Required fields are marked *