Click here to Skip to main content
15,878,852 members
Articles / Web Development / ASP.NET
Article

Simple NHibernate Architecture

Rate me:
Please Sign up or sign in to vote.
4.91/5 (38 votes)
11 Sep 2012CPOL3 min read 133K   3.2K   145   23
An article showing a nice architecture I've come up for using NHibernate

Introduction

I started using NHibernate at the end of last year and I am having a wonderful experience with it. The simple fact that I don't have to maintain hundreds of procedures and data access abstraction classes to do basic CRUD operations is enough to justify the use of NHibernate (even though I used a custom code generation tool). Besides that, my code is amazingly clearer and simpler. I only need to worry about business and user interface logic now. All that pluming code is gone for good.

I had zero experience with ORM frameworks, so on the first couple of weeks using NHibernate I had a hard time with some issues, like using it with ASP.NET disconnected objects and abstracting NHibernate sessions from the domain layer. After putting in some effort I came out with a nice architecture that abstracts the NHibernate layer and allows me to work with an ASP.NET session.

Background

I am assuming you already have some familiarity with NHibernate. If you are new to NHibernate I encourage you to go to the website and download the reference documentation. I am using NHibernate 1.2 CR1 in this article.

The Architecture

The idea here is to keep my business objects unaware of an NHibernate session. So I created a different project for NHibernate stuff, called Shared.NHibernateDAL. It includes the session and transaction manager classes. I also created a common business object that serves as a base class for all my business entities. This class (BusinessObject) encapsulates the common methods and properties all entities that need to be persisted have, like Save(), Delete(), ID, etc.

Here's a class diagram just to give an idea:

Screenshot - nh_classdiagram.jpg

The Implementation

The main classes on the NHibernateDAL project are the following:

  • NHibernateSessionManager – This is the class responsible for managing the NHibernate session. I've got this class from another NHibernate article on Code Project.

  • TransactionBlock – This is a class I created to manage transactions and abstract it from NHibernate. I will show an example of its use further on this article.

  • BusinessObject<T> - This is the base class for all the business entities I need to persist. It implements the basic CRUD methods.

Here's the code:

C#
namespace Shared.NHibernateDAL
{
    public class BusinessObject<T>
    {
        public virtual void Save()
        {
            ISession session = NHibernateSessionManager.Instance.GetSession();
            if (!this.IsPersisted)
                session.Save(this);
            else
                session.SaveOrUpdateCopy(this);

            session.Flush();
        }

        public virtual void Delete()
        {            
            ISession session = NHibernateSessionManager.Instance.GetSession();
            session.Delete(this);
            session.Flush();
        }

        public virtual T Clone()
        {            
            return this.Clone();
        }

        /// <summary>

        /// Returns a valid populated object or a null reference        
        /// </summary>

        public static T Get(int id)
        {
            if (id <= 0)
                return default(T);
            else
            {
                ISession session = 
                    NHibernateSessionManager.Instance.GetSession();
                return session.Get<T>(id);
            }
        }

        private int _id;

        public virtual int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        public virtual bool IsPersisted
        {
            get
            {
                return this.ID > 0;
            }
        }
    }
}

Using the Code

On this sample we are going to use a Product class to show the features.

Here's the Product class code:

C#
namespace Business
{
public class Product : BusinessObject<Product>
{
    public Product()
    {
        this._name = null;
        this._weight = 0;
    }

    private string _name;
    private decimal _weight;

    public virtual decimal Weight
    {
        get { return _weight; }
        set { _weight = value; }
    }

    public virtual string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public static List<Product> Select()
    {
        ISession session = NHibernateSessionManager.Instance.GetSession();
        IQuery query = session.CreateQuery("from Product");
        return (List<Product>)query.List<Product>();
    }
}
}

Because all CRUD functionality is encapsulated on the BusinessObject, the domain entities are as simple as that. Now our Product class is ready to be consumed.

Note that I have included a static Select method that returns a list of products. This might be useful for populating user interface controls, but if you want to completely abstract NHibernate from your domain objects you should create another layer of abstraction under the business layer, containing all HQL queries. But, for small projects, I think it is OK to use HQL inside business objects.

The download file contains three projects, corresponding to the three layers I am using: NHibernateDAL, Business and Web. The Web project is a simple web page showing how easy it is to consume CRUD operations from the Product class.

Transactions

It is often necessary to use nested transactions with objects. You might have a method that opens its own transaction and sometimes needs to be called from another transaction. To solve this problem and also abstract the NHibernate transaction methods I've come out with the TransactionBlock class. This class keeps track of how many transaction blocks were open and ensures that only one transaction is opened on the database. At the end it commits the transaction only if all blocks were declared valid. Here's an example of its usage:

C#
public void OutterTransaction()
{
    using (TransactionBlock tran = new TransactionBlock())
    {
        Product p1 = new Product();
        p1.Name = "Product 1";
        p1.Weight = 10;
        p1.Save();

        //Call another method with a transaction block
        InnerTransaction();

        tran.IsValid = true;
        // On the dispose call the database transaction is commited 
        //if all transaction blocks are valid
    }
}

private void InnerTransaction()
{
    using (TransactionBlock tran = new TransactionBlock())
    {
        Product p2 = new Product();
        p2.Name = "Product 2";
        p2.Weight = 10;
        p2.Save();                
        tran.IsValid = true;
    }
}

Conclusion

NHibernate has definitely helped me improve the quality and readability of my code. Combined with code generation tools (like MyGeneration), it can really improve productivity. But be aware, NHibernate and the architecture I am using will not fit every project. Working with legacy database can be a pain, although version 1.2 supports communication with stored procedures.

Any feedback will be greatly appreciated.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Intelligent Coder
Canada Canada
I've been developing .NET enterprise applications since 2000.

I am originally from Rio de Janeiro and I am currently working at http://www.intelligentcoder.com in Ontario.

I also have my own startup where we offer client intake forms.

Comments and Discussions

 
GeneralMy vote of 5 Pin
csharpbd2-May-13 20:59
professionalcsharpbd2-May-13 20:59 
GeneralMy vote of 5 Pin
Member 313707823-Sep-12 20:03
Member 313707823-Sep-12 20:03 
QuestionListing product from Business Class Pin
bjorkenne17-Sep-12 8:54
bjorkenne17-Sep-12 8:54 
GeneralMy vote of 5 Pin
Monjurul Habib11-Sep-12 9:45
professionalMonjurul Habib11-Sep-12 9:45 
GeneralMy vote of 4 Pin
Shahriar Iqbal Chowdhury/Galib2-Jul-10 22:54
professionalShahriar Iqbal Chowdhury/Galib2-Jul-10 22:54 
QuestionI test the code but I have a problem Pin
benkhaled lotfi26-Mar-09 5:38
benkhaled lotfi26-Mar-09 5:38 
GeneralNew to NHibernate where is the NHibernate database file Pin
arigney12-Feb-08 17:51
arigney12-Feb-08 17:51 
GeneralWatch out for CallContext in TransactionBlock Pin
Chuck Kinnan16-Aug-07 12:00
Chuck Kinnan16-Aug-07 12:00 
GeneralRe: Watch out for CallContext in TransactionBlock Pin
Cassio Mosqueira17-Aug-07 4:59
Cassio Mosqueira17-Aug-07 4:59 
GeneralRe: Watch out for CallContext in TransactionBlock Pin
Chuck Kinnan17-Aug-07 6:14
Chuck Kinnan17-Aug-07 6:14 
GeneralWinforms Pin
Juan Carlos Delgado10-May-07 14:13
Juan Carlos Delgado10-May-07 14:13 
GeneralRe: Winforms Pin
Cassio Mosqueira15-May-07 2:11
Cassio Mosqueira15-May-07 2:11 
GeneralDB Object != Business Object Pin
blorq24-Apr-07 9:30
blorq24-Apr-07 9:30 
GeneralGood article, but I disagree Pin
Sean Chambers17-Apr-07 2:02
Sean Chambers17-Apr-07 2:02 
GeneralRe: Good article, but I disagree Pin
Craig G. Wilson17-Apr-07 2:37
Craig G. Wilson17-Apr-07 2:37 
Sean, I agree with you. I too place my repository classes in a seperate library to avoid dependencies and I too use Castle's microkernel/windsor for IoC.

However, you have contradicted yourself. You said that using the base class (or other methods) creates a tightly coupled architecture with nHibernate... however, you also stated that using Castle's ActiveRecord was ok??? ActiveRecord relies on attributes that are tightly coupled with Castle.ActiveRecord, which in turn is tightly coupled with nHibernate.
GeneralRe: Good article, but I disagree Pin
Sean Chambers17-Apr-07 3:12
Sean Chambers17-Apr-07 3:12 
GeneralRe: Good article, but I disagree Pin
Billy McCafferty17-Apr-07 10:29
Billy McCafferty17-Apr-07 10:29 
GeneralRe: Good article, but I disagree Pin
Cassio Mosqueira18-Apr-07 4:55
Cassio Mosqueira18-Apr-07 4:55 
GeneralRe: Good article, but I disagree Pin
balazs_hideghety17-Jun-07 20:30
balazs_hideghety17-Jun-07 20:30 
GeneralRe: Good article, but I disagree Pin
nmirandaghn26-Aug-08 18:42
nmirandaghn26-Aug-08 18:42 
GeneralRe: Good article, but I disagree Pin
timtas26-Apr-07 7:42
timtas26-Apr-07 7:42 
GeneralQuestion about B.O. to table mapping Pin
Marc Clifton17-Apr-07 1:13
mvaMarc Clifton17-Apr-07 1:13 
AnswerRe: Question about B.O. to table mapping Pin
Davide Icardi9-May-07 1:47
Davide Icardi9-May-07 1:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.