6
\$\begingroup\$

Using a combination provided from this example and this implementation, I'm creating a solution that decouples the UnitOfWork class from the individual repositories, as they violate the Open-Closed Principle: every time you added a new repository you would have to modify the UnitOfWork class. I am using Unity 3.0 as the IoC container to wire up dependencies, together with Unity Bootstrapper for ASP.NET MVC and its PerRequestLifetimeManager to handle automatically disposing the UnitOfWork and DbContextFactory classes.

Here's my proposed demo solution:

Repositories

public interface IRepository<TEntity> where TEntity : class
{
    TEntity Create();
    // omitted for brevity
}

public class Repository<TEntity> : IRepository<TEntity>
    where TEntity : class
{       
    private readonly DbContext _context;

    public Repository(IUnitOfWork uow)
    {
        _context = uow.Context;
    }

    public virtual TEntity Create(TEntity entity)
    {
        return _context.Set<TEntity>().Add(entity);         
    }   

    // omitted for brevity      
}

public interface IEmployeeRepository : IRepository<Employee>
{
}

public interface ICustomerRepository : IRepository<Customer>
{
}

public class EmployeeRepository : Repository<Employee>
{
    public EmployeeRepository(IUnitOfWork uow)
        : base(uow)
    {
    }
}

public class CustomerRepository : Repository<Customer>
{
    public CustomerRepository(IUnitOfWork uow)
        : base(uow)
    {
    }
}

DbContext Factory

public interface IDbContextFactory
{
    DbContext GetContext();
}

public class DbContextFactory : IDbContextFactory
{
    private readonly DbContext _context;

    public DbContextFactory()
    {
        _context = new MyDbContext("ConnectionStringName");
    }

    public DbContext GetContext()
    {
        return _context;
    }
}

Unit Of Work

public interface IUnitOfWork
{
    void SaveChanges();
    DbContext Context { get; }
}

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private readonly DbContext _context;
    private bool disposed = false;

    public UnitOfWork(IDbContextFactory contextFactory)
    {
        _context = contextFactory.GetContext();
    }

    public void SaveChanges()
    {
        if (_context != null)
        {
            _context.SaveChanges();
        }
    }

    public DbContext Context
    {
        get { return _context; }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Service

public class CompanyService
{
    private readonly IUnitOfWork _uow;
    private readonly IEmployeeRepository _employeeRepository;
    private readonly ICustomerRepository _customerRepository;

    public CompanyService(IUnitOfWork uow, IEmployeeRepository employeeRepository, ICustomerRepository customerRepository)
    {           
        _uow = uow;
        _employeeRepository = employeeRepository;
        _customerRepository = customerRepository;
    }

    // over-simplified example method of using 2 separate repositories
    public void AddEmployeeAndCustomer()
    {
        _employeeRepository.Create(new Employee {Id = 1, Name = "Test Employee"});
        _customerRepository.Create(new Customer { Id = 2, Name = "Test Customer" });

        _uow.SaveChanges();
    }   

    // over-simplified example method of getting all employees
    public List<Employee> GetEmployess()
    {
        return _employeeRepository.Create(new Employee {Id = 1, Name = "Test Employee"});           
    }

}

Does anyone have any input with regards to this solution - perhaps a "better" way? The benefit by injecting the IUnitOfWork, IEmployeeRepository and ICustomerRepository dependancies into the service layers is being able to access the repositories directly, without having to go through the UnitOfWork class to access a repository (.e.g., _uow.EmployeeRepository.FindAll()), but also being able to call the UnitOfWork.SaveChanges() method for insert/updates/deletes when needed.

One concern with using the PerRequestLifetimeManager: "Although the PerRequestLifetimeManager class works correctly and can help you to work with stateful or thread-unsafe dependencies within the scope of an HTTP request, it is generally not a good idea to use it if you can avoid it. Using this lifetime manager can lead to bad practices or hard to find bugs in the end user’s application code when used incorrectly."

An alternative solution could be to store the DbContext within the System.Web.HttpContext.Items collection.

\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

While I'm not sure of the safety of PerRequestLifetimeManager, one thing I'd recommend changing in your repository is to avoid inheritance from Repository<T>.

Instead of

public class EmployeeRepository : Repository<Employee>
{
    ...
}

do

public class EmployeeRepository : IEmployeeRepository
{
    private Repository<Employee> _genericRepo;
    ...
}

When you don't want to implement all methods of Repository<T> in your EmployeeRepository (like say, you don't want to delete Employees), it leads to cleaner code. You avoid exposing the unwanted interface, you avoid throwing exceptions if the Delete method is called. And instead of calling base.SomeMethod(), you call _genericRepo.SomeMethod(), thus getting the same advantage of code re-use.

\$\endgroup\$
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.