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.