What is the correct way to use Unit of Work/Repositories within the business layer?

2.7k views Asked by At

Having built a small application using the Unit of Work/Repository pattern, I am struggling to understand how to use this properly within my business layer. My application has a a data access layer which can be either NHibernate or the Entity Framework. I can switch between these easily.

I have a number of repositories, for example, Customer, Order etc. My unit of work will be either an ISession or an Object Context depending on which DAL I want to test with.

My business layer contains a single business method - CreateOrder(). What I am struggling to understand is where in the business layer I should be initialising my unit of work and my repositories.

Focusing on Nhibernate, my DAL looks like:

public class NHibernateDAL : IUnitOfWork
{
    log4net.ILog log = log4net.LogManager.GetLogger(typeof(NHibernateDAL));
    ISession context;

    public NHibernateDAL()
    {            
        context = SessionProvider.OpenSession();
        this.Context.BeginTransaction();
        CurrentSessionContext.Bind(context);            
    }

    public ISession Context
    {
        get { return context; }
    }

    public void Commit()
    {           
        this.Context.Transaction.Commit();
        context.Close();            
    }

    public void Dispose()
    {
        ISession session = CurrentSessionContext.Unbind(SessionProvider.SessionFactory);
        session.Close();
    }
}

Within my business layer, I want to know where I should be declaring my Unit of Work and repositories. Are they declared at class level or within the CreateOrder method?

For example:

public class BusinessLogic
{         
    UnitOfWork _unitOfWork = new UnitOfWork(NHibernateDAL);
    NhRepository<Order> _orderRepository = new NhRepository<Order>(_unitOfWork);    
    NhRepository<Customer> _customerRepository = new NhRepository<Customer>(_unitOfWork);
    ....

    public void CreateOrder(.....)
    {
         Order order = new Order();
         _orderRepository.Add(order);

         _unitOfWork.Commit();
     }
}

The above code works only for the first time the CreateOrder() method is called, but not for subsequent calls because the session is closed. I have tried removing the 'context.Close()' call after committing the transaction but this also fails. Although the above approach doesn't work, it seems more correct to me to declare my repositories and unit of work with this scope.

However, if I implement it as below instead it works fine, but it seems unnatural to declare the repositories and unit of work within the scope of the method itself. If I had a tonne of business methods then I would be declaring repositories and Units of Work all over the place:

public class BusinessLogic
{                      
    public void CreateOrder(.....)
    {
         UnitOfWork _unitOfWork = new UnitOfWork(NHibernateDAL);
         var _orderRepository = new NhRepository<Order>(_unitOfWork);    

         NhRepository<Customer> _customerRepository = null;
         Order order = new Order();
         _orderRepository.Add(order);             

         _unitOfWork.Commit();
     }
}

If I were to implement this with class level declaration then I think I would need some means of re-opening the same unit of work at the start of the CreateOrder method.

What is the correct way to use the unit of work and repositories within the business layer?

2

There are 2 answers

0
Elisabeth On

Your IUnitOfWork implementation contains all repositories.

Your IUnitOfWork is injected into your presentation layer like mvc controller.

Your IUnitOfWork is injected into mvc controller.

Your IRepository is injected into your UnitOfWork implementation.

0
SASS_Shooter On

Looks to me like you've almost got it. In our new server stack I have this setup:

WCF Service Layer  --> just returns results from my Business Layer

My business layer is called, creates a unitofwork, creates the respository
Calls the respository function
Uses AutoMapper to move returned results into a DTO

My repository gets the query results and populates a composite object.

Looks almost like what you've got there. Though we use Unity to locate what you call the business layer. (we just call it our function processor)

What I would highly suggest, though, is that you do NOT keep the UnitOfWork at the class level. After all each descreet function is a unit of work. So mine is like this (the names have been changed to protect the innocent):

        using ( UnitOfWorkScope scope = new UnitOfWorkScope( TransactionMode.Default ) )
        {
            ProcessRepository repository = new ProcessRepository(  );
            CompositionResultSet result = repository.Get( key );
            scope.Commit( );

            MapData( );
            return AutoMapper.Mapper.Map<ProcessSetDTO>( result );
        }

We also had a long discussion on when to do a scope.Commit and while it isn't needed for queries, it establishes a consistent pattern for every function in the application layer. BTW we are using NCommon for our repository/unitofwork patterns and do not have to pass the UoW to the repository.