Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 501
JMc Avatar asked Apr 07 '11 11:04

JMc


People also ask

How do you use the unit of work pattern?

Unit of Work in the Repository Pattern This means, one unit of work here involves insert/update/delete operations, all in one single transaction. To understand this concept, consider the following implementation of the Repository Pattern using a non-generic repository, for a Customer entity.

What is the use of unit of work?

The unit of work class serves one purpose: to make sure that when you use multiple repositories, they share a single database context. That way, when a unit of work is complete you can call the SaveChanges method on that instance of the context and be assured that all related changes will be coordinated.

What is the right way to include a repository into a controller?

By taking advantage of dependency injection (DI), repositories can be injected into a controller's constructor. the following diagram shows the relationship between the repository and Entity Framework data context, in which MVC controllers interact with the repository rather than directly with Entity Framework.

What is the use of Repositories?

Basically, a repository allows you to populate data in memory that comes from the database in the form of the domain entities. Once the entities are in memory, they can be changed and then persisted back to the database through transactions.


1 Answers

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.

like image 122
SASS_Shooter Avatar answered Sep 22 '22 13:09

SASS_Shooter