Creating a Web Application Using MVC, Unity and NHibernate – Part 1

Tuesday, January 13, 2009

 

Introduction

 

I thought it would be useful to document what it would take to incorporate building a web application that is designed with the Microsoft MVC framework, that also incorporated using NHibernate as a O/R Mapper and uses Microsoft Unity as the Dependency Injection, IoC framework.  I must confess, I am not an expert of any of these tools, so I welcome feedback from the community.  The reason I am doing this to begin with, is there is not a whole a lot of documentation on of this “stuff” by themselves let alone all together, so I am hoping fill a little bit of that void.  If you think there are better approaches then what I am doing, feel free to provide feedback.   I am basically doing this for my own enrichment and if it is also helpful to the community, then—well—even better.

Since this going to have to be a series of posts, I will probably not cover every aspect of this application all at once, so if what you are looking for is not in this post, then be patient and maybe I will get to it in a later one.  As a matter of fact, since I am going to be adhering to a test first approach (red, green, refactor), I will probably not get to the MVC framework until several posts from now.  The first few posts will only be covering tests.

 

Setting up NHibernate

 

I have mentioned this before, but if you are new to NHibernate and don’t know how to get started, I highly recommend the Summer of NHibernate videos by Stephen Bohlen.  The download of NHibernate is located here.  Once downloaded, the first thing I need to do is put the NHibernate schema files in the Visual Studio schema folder (my folder is located here C:\Program Files\Microsoft Visual Studio 9.0\Xml\Schemas) so I can get intellisense on the configuration and mapping files. 

In general, for all the external libraries, it is useful to place them all in a folder location relative to, or just inside your solution so, for example, if you are using source control the other developer machines will pick up the references without any problems.  Thus, I am doing the same with this application, and will then be referencing the NHibernate.dll in all my relevant projects.  In my case, I am only going to have a Web, Core, and Test project so I will reference it for now in the Core and Test project.  I will probably need to reference in the web project once I set up Unity, but I will leave it out for now.

The next thing I will need to do is to create a NHibernate configuration file.  One of the cool concepts NHibernate follows is the Convention over Configuration paradigm.  That is, as long as I follow a certain convention, I will not need to configure certain aspects of NHibernate when setting it up.  So if I create a configuration file and name it hibernate.cfg.xml, then set is build action to copy to output folder, I will not need to tell NHibernate where the configuration file is.  Note:  I tried this in a Microsoft Team Test project and for some reason the test project would not copy that file to the output folder so I ended up having to configure the path anyway.

Here is the configuration file that is in the root directory of my test project.

    1 <?xml version="1.0" encoding="utf-8" ?>

    2 <hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">

    3   <session-factory name="NHibernate.Test">

    4     <property name="connection.driver_class">NHibernate.Driver.SqlClientDriver</property>

    5     <property name="connection.connection_string">

    6       Data Source=(local);Initial Catalog=News;Persist Security Info=True;User ID=my_dev;Password=my_dev

    7     </property>

    8     <property name="adonet.batch_size">10</property>

    9     <property name="show_sql">true</property>

   10     <property name="dialect">NHibernate.Dialect.MsSql2005Dialect</property>

   11     <property name="use_outer_join">true</property>

   12     <property name="command_timeout">444</property>

   13     <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>

   14     <mapping assembly="News.Core"/>

   15   </session-factory>

   16 </hibernate-configuration>

 

Notice that in the root tag that since I place the NHibernate schema files in the Visual Studio schema folder I now have access to intellisense.  You can see the property setting definitions here, but here are the important nodes

·          The show_sql property is going to be useful when debugging so I can see the SQL statements.  I will turn this off in production because it is expensive.

·          The dialect is going to tell NHibernate to what specific database language and version it is going to translate the SQL to.

·          The mapping tells NHibernate where the mapping files and classes are located.

Once the configuration file is created, I then can call it from my code and create the ISessionFactory.  I will talk about my approach later in another post, but essentially because ISessionFactory is expensive to create and there also some threading concerns with creating it; I am going to create it differently in the test project than in the web project.  In both cases, I am going to implement an interface I created call ISessionFactoryManager.  This interface, as of now, will have one method called GetSessionFactory.  The code in my test project looks like this.

    1 using News.Core.Data;

    2 using NHibernate;

    3 using NHibernate.Cfg;

    4 

    5 namespace News.Web.Tests

    6 {

    7     internal class TestSessionFactoryManager : ISessionFactoryManager

    8     {

    9         public ISessionFactory GetSessionFactory()

   10         {

   11             string path = @"C:\Projects\News\Src\News.Web.Tests\hibernate.cfg.xml";

   12             var cfg = new Configuration();

   13             cfg.Configure(path);

   14             return cfg.BuildSessionFactory();

   15 

   16         }

   17     }

   18 }

 

 Note:  If I want, I can also add properties at run time.  For example, say my connection string is stored in a super secret place; I could get it and set the ISessionFactory with the following code.  It just has to be done before the BuildSessionFactory is called, because once called, it cannot be changed.

  15             cfg.Properties.Add("connection.connection_string",connectionString);

 

Once the BuildSessionFactory method is called, Nhibernate goes and retrieves the configuration file, and also the Mapping files (I will discuss later) and returns the session factory.  I now can open sessions to the database and do whatever database CRUD I need to do.

Mapping Tables to Classes

 For my example, I am going to have two tables with the following schema:


For this post, I am only going to worry about the News table.  Again, using convention over configuration, I have mapping file named NewsItemDto.hbm.xml so I do not have to tell NHibernate where it is.  I need to make sure this file is an embedded resource, so it can be referenced.  The content of the file looks like this:

    1 <?xml version="1.0" encoding="utf-8" ?>

    2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="News.Core" namespace="News.Core.Dto">

    3   <class name="News.Core.Dto.NewsItemDto, News.Core" table="News">

    4     <id column="NewsId" name="NewsId" type="long" unsaved-value="0">

    5       <generator class="native"></generator>

    6     </id>

    7     <property column="AuthorId" name="AuthorId" type="long" not-null="true"/>

    8     <property column="DateAdded" name="DateAdded" type="DateTime" not-null="true"/>

    9     <property column="DateUpdated" name="DateUpdated" type="DateTime" not-null="true"/>

   10     <property column="DatePublished" name ="DatePublished" type="DateTime" not-null="true" />

   11     <property column="Title" name="Title" type="string" not-null="true"/>

   12     <property column="ShortDescription" name="ShortDescription" type="string" not-null="false"/>

   13     <property column="Body" name="Body" type="string" not-null="true"/>

   14     <property column="IsFrontPage" name="IsFrontPage"/>

   15     <property column="IsPublished" name="IsPublished"/>

   16   </class>

   17 </hibernate-mapping>

 

Again, the root references the NHibernate mapping schema which will give me intellisense.  You can see all the property settings here.  In the class, I tell it the namespace and assembly name where the class is located.  The ID identifies the primary key of the table.  By setting the generator attribute to native, I am telling NHibernate that the field is an identity field and the value is created by the database.

Here is the code for the class:

    5     public class NewsItemDto

    6     {

    7         public virtual long NewsId { get; set; }

    8 

    9         public virtual long AuthorId { get; set; }

   10 

   11         public virtual DateTime DateAdded { get; set; }

   12 

   13         public virtual DateTime DatePublished { get; set; }

   14 

   15         public virtual DateTime DateUpdated { get; set; }

   16 

   17         public virtual string Title { get; set; }

   18 

   19         public virtual string ShortDescription { get; set; }

   20 

   21         public virtual string Body { get; set; }

   22 

   23         public virtual bool IsFrontPage { get; set; }

   24 

   25         public virtual bool IsPublished { get; set; }

   26     }

 

The properties are all virtual so NHibernate can utilize the proxy pattern for performance reasons.  This will come more into play when use the Authors table in following posts.

 

Creating the test

 

As a part of the buildup and teardown of my NHibernate tests, I will have each of my test create a session object that connects to the data base before the test starts and then close the session object once the test finishes.

    1 using News.Core.Data;

    2 using Microsoft.VisualStudio.TestTools.UnitTesting;

    3 using NHibernate;

    4 

    5 namespace News.Web.Tests

    6 {

    7     public class DatabaseBaseTest

    8     {

    9         private ISessionFactoryManager sessionFactoryManager = new TestSessionFactoryManager();

   10         protected ISession session;

   11 

   12 

   13 

   14         [TestInitialize]

   15         public void SetUp()

   16         {

   17             

   19             session = sessionFactoryManager.GetSessionFactory().OpenSession();

   20         }

   21 

   22         [TestCleanup]

   23         public void CleanUp()

   24         {

   25             session.Close();

   26             session = null;

   27         }

   28     }

   29 }

 

Now that I have Session I can create my first test.  This test will simply return 1 record from the NewsItem table by passing the NewsItemId parameter.

   78         [TestMethod()]

   79         public void GetNewsItemByItemIdShouldReturnMatchingTitle()

   80         {

   81             var target = new NHibernateDataProvider(session);

   82             const string expected = "test";

   83             const int itemId = 2;

   84             Assert.AreEqual(expected, target.GetNewsItemByItemId(itemId).Title);

   85         }

 

My database has a record which contains the Title value “test”.

 

The Repository

 

The data access code that gets the records looks like this:

   17         public NewsItemDto GetNewsItemByItemId(long newsItemId)

   18         {

   19             return _session.Get<NewsItemDto>(newsItemId);

   20 

   21         }

 

 

In the next post I will add the Authors table to the mix and some functionality retrieving those joined records.

comments powered by Disqus