Home
Gallery
GuestBook
SourceCode
Contact

MPBlog Implementation. Part 4

Posted in .NET
This post has been read 11536 times

NHibernate Repository and Unit Of Work

In my previous post we looked at the domain model and the class maps associated with it. In this post lets look at the Repository and Unit Of Work implementations.

Note: The following is based on the FubuMVC contrib project.

IRepository

A repository is an abstraction layer which gives your application an in memory domain object collection. This layer helps in making you application agnostic about where the data is coming from or where it is persisted into. It could well be a flat file or any other RDBMS. Preferably each of your domain object would have its own repository. But for the sake of keeping it simple, I am using a generic repository in the following code.

namespace MPBlog.Core.BaseClasses

{

    using System;

    using System.Linq;

    using System.Linq.Expressions;

 

    public interface IRepository

    {

        T Load<T>( int id ) where T : Entity<T>;

        void Delete<T>( T target );

        void Save<T>( T target );

        IQueryable<T> Query<T>() where T : Entity<T>;

        IQueryable<T> Query<T>( Expression<Func<T, bool>> whereQuery ) where T : Entity<T>;

    }

}

The Query methods here will be implemented using Linq to NHibernate. Now that we have the interface, lets look at the implementation.

NHRepository

namespace MPBlog.Persistence.RepositoryImpl

{

    using System;

    using System.Linq;

    using System.Linq.Expressions;

    using Core.BaseClasses;

    using NHibernate.Linq;

    using UnitOfWork;

 

    public class NHRepository : IRepository

    {

        private INHibernateUnitOfWork _unitOfWork;

 

        public NHRepository( INHibernateUnitOfWork unitOfWork )

        {

            _unitOfWork = unitOfWork;

        }

 

        public T Load<T>( int id ) where T : Entity<T>

        {

            return _unitOfWork.Session.Load<T>( id );

        }

 

        public void Delete<T>(T target)

        {

            _unitOfWork.Session.Delete( target );

        }

 

        public void Save<T>(T target)

        {

            _unitOfWork.Session.Save( target );

        }

 

        public IQueryable<T> Query<T>() where T : Entity<T>

        {

            return _unitOfWork.Session.Linq<T>();

        }

 

        public IQueryable<T> Query<T>(Expression<Func<T, bool>> whereQuery) where T : Entity<T>

        {

            return _unitOfWork.Session.Linq<T>().Where( whereQuery );

        }

    }

}

If you notice the above code, we are accessing the session through the unitofwork. A unit of work helps in keeping track of all the objects that are affect by a business transaction. Following is the implementation of the UnitOfWork.

IUnitOfWork and INHibernateUnitOfWork

namespace MPBlog.Core.BaseClasses

{

    using System;

 

    public interface IUnitOfWork : IDisposable

    {

        void Initialize();

        void Commit();

        void Rollback();

    }

}

namespace MPBlog.Persistence.UnitOfWork

{

    using Core.BaseClasses;

    using NHibernate;

 

    public interface INHibernateUnitOfWork : IUnitOfWork

    {

        ISession Session { get; }

    }

}

NHibernateUnitOfWork

namespace MPBlog.Persistence.UnitOfWork

{

    using System;

    using FluentNHibernate;

    using NHibernate;

 

    public class NHibernateUnitOfWork : INHibernateUnitOfWork

    {

        private ITransaction _transaction;

        private readonly ISessionSource _source;

        private bool _isDisposed;

        private bool _isInitialized;

 

        public ISession Session { get; private set; }

 

        public NHibernateUnitOfWork( ISessionSource source )

        {

            _source = source;

        }

 

        public void Initialize()

        {

            CheckUoWSanity();

 

            Session = _source.CreateSession();

            StartNewTransaction();

 

            _isInitialized = true;

        }

 

        public void Commit()

        {

            CheckUoWSanity();

            CheckUoWInitialization();

 

            _transaction.Commit();

 

            StartNewTransaction();

        }

 

        public void Rollback()

        {

            CheckUoWSanity();

            CheckUoWInitialization();

 

            _transaction.Rollback();

 

            StartNewTransaction();

        }

 

        public void Dispose()

        {

            if( _isDisposed || !_isInitialized )

                return;

 

            _transaction.Dispose();

            Session.Dispose();

 

            _isDisposed = true;

        }

 

        private void CheckUoWSanity()

        {

            if( _isDisposed )

            {

                throw new ObjectDisposedException( "Trying to use disposed object" );

            }

        }

 

        private void CheckUoWInitialization()

        {

            if( !_isInitialized )

            {

                throw new InvalidOperationException( "NHibernateUnitOfWork is not initialized" );

            }

        }

 

        private void StartNewTransaction()

        {

            if( _transaction != null )

            {

                _transaction.Dispose();

            }

 

            _transaction = Session.BeginTransaction();

        }

    }

}

Tests

Following are the test that were written for both the repository and unitofwork. I am using MoQ as my mocking framework so that I don't have to connect to the actual database when testing.

namespace MPBlog.Tests.Persistence.Tests

{

    using System.Linq;

    using Core.Domain;

    using Moq;

    using MPBlog.Persistence.RepositoryImpl;

    using MPBlog.Persistence.UnitOfWork;

    using NHibernate;

    using Xunit;

 

    public class RepositoryTests

    {

        private Mock<ISession> _session;

        private Mock<INHibernateUnitOfWork> _uow;

        private Mock<NHRepository> _repo;

 

        public RepositoryTests()

        {

            _session = new Mock<ISession>();

            _uow = new Mock<INHibernateUnitOfWork>();

            _uow.Setup( u => u.Session ).Returns( _session.Object );

            _repo = new Mock<NHRepository>( _uow.Object );

        }

 

        [Fact]

        public void Call_to_load_should_load_object_from_the_session()

        {

            var postId = 1;

            _repo.Object.Load<Post>( postId );

            _session.Verify( s => s.Load<Post>( postId ), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Call_to_save_should_save_object_on_the_session()

        {

            var post = new Post();

            _repo.Object.Save( post );

            _session.Verify( s => s.SaveOrUpdate( post ), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Call_to_delete_should_delete_object_from_the_session()

        {

            var post = new Post();

            _repo.Object.Delete( post );

            _session.Verify( s => s.Delete( post ), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Call_to_query_on_session_should_return_an_IQueryable_object()

        {

            Assert.IsAssignableFrom( typeof( IQueryable<Post> ), _repo.Object.Query<Post>() );

        }

    }

}

namespace MPBlog.Tests.Persistence.Tests

{

    using FluentNHibernate;

    using Moq;

    using MPBlog.Persistence.UnitOfWork;

    using NHibernate;

    using Xunit;

 

    public class UnitOfWorkTests

    {

        protected Mock<ISession> _session;

        protected Mock<ITransaction> _transaction;

        protected Mock<ISessionSource> _sessionSource;

        protected Mock<NHibernateUnitOfWork> _uow;

 

        public UnitOfWorkTests()

        {

            _session = new Mock<ISession>();

            _transaction = new Mock<ITransaction>();

            _sessionSource = new Mock<ISessionSource>();

 

            _sessionSource.Setup( s => s.CreateSession() ).Returns( _session.Object );

            _session.Setup( s => s.BeginTransaction() ).Returns( _transaction.Object );

 

            _uow = new Mock<NHibernateUnitOfWork>( _sessionSource.Object );

            _uow.Object.Initialize();

        }

 

        [Fact]

        public void Can_create_new_transaction()

        {

            _session.Verify( s => s.BeginTransaction(), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Can_dispose_the_transaction()

        {

            _uow.Object.Dispose();

            _transaction.Verify( s => s.Dispose(), Times.Exactly( 1 ) );

        }

 

        [Fact]

        public void Can_dispose_the_session()

        {

            _uow.Object.Dispose();

            _session.Verify( s => s.Dispose(), Times.Exactly( 1 ) );

        }

 

        [Fact]

        public void Call_to_commit_should_commit_the_transaction()

        {

            _uow.Object.Commit();

            _transaction.Verify( t => t.Commit(), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Call_to_commit_should_dispose_the_transaction_and_start_a_new_transaction()

        {

            _uow.Object.Commit();

            _transaction.Verify( t => t.Dispose(), Times.Exactly( 1 ) );

            _session.Verify( s => s.BeginTransaction(), Times.Exactly( 2 ) );

        }

 

        [Fact]

        public void Call_to_rollback_should_rollback_the_transaction()

        {

            _uow.Object.Rollback();

            _transaction.Verify( t => t.Rollback(), Times.AtLeastOnce() );

        }

 

        [Fact]

        public void Call_to_rollback_should_dispose_the_transaction_and_start_a_new_transaction()

        {

            _uow.Object.Rollback();

            _transaction.Verify( t => t.Dispose(), Times.Exactly( 1 ) );

            _session.Verify( s => s.BeginTransaction(), Times.Exactly( 2 ) );

        }

    }

}




Comment posted on Thursday, September 17, 2009 10:37 AM
mmm... start a transaction at the begin of the UoW and commit it at the end is not a good practice.
The UoW is implemented by NH's and, depending on the pattern you are using to manage the session you may have one session with multiple transactions or request-session-transaction with the same life cycle. If you are using session-per-request there is no problem but don't use your UoW implementation with another pattern. Take care.
Comment posted on Friday, September 18, 2009 9:22 AM
@Fabio

I had used the same thing in one of the projects and I was getting this error which says request should come with valid transaction descriptor. I think the problem lies in what you have mentioned.. Henceforth will avoid it. Thanks for the tip
Comment posted on Thursday, June 14, 2012 7:49 AM
Really nice article Internet is the best place where you can gather information about the poor credit card offers.When a person searches on the Internet with the keywords “zero fee bad credit cards”,then he will get a thousand of results about the companies which come with such offering.
Comment posted on Tuesday, August 21, 2012 11:38 PM
I think that this is the first website that impress me a lot with it's content,  simple design
Name:
E-mail:
Website:
Comment:
 
Anti Bot Image:

Insert Cancel


Subscribe

Random Photo

My Tweets


Top Posts

Source Code

The source code to this site is open-source. You can download the code from here.

Categories


Recent Blogs


Archives


Blogroll