Entity Framework using Repository Pattern, Unit of

2019-01-30 17:07发布

问题:

Using a combination provided from this example and this implementation I am trying to create a solution that decouples the UnitOfWork class from the individual repositories, as they violate the Open-Closed Principle, since every time you added a new repository you would have to modify the UnitOfWork class. I am using Unity as the IoC container to wire up dependencies.

The problem I have is that in automatically wiring up the UnitOfWork, IDbContext and the repositories (IEmployeeRepository and ICustomerRepository) using Unity, the repositories will be injected with separate instances of the UnitOfWork, which, of course, defeats the purpose. I need to share the context across the repositories, and it seems I am missing a piece to this puzzle - at the moment (see Service layer) the UnitOfWork instantiated will be different to the UnitOfWork for each of repositories.

How do inject the IUnitOfWork into the service layer and pass this instantiated shared UnitOfWork class to the respective repositories, using Unity and dependency injection?

Here's my proposed (fabricated) 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
    public void AddEmployeeAndCustomer()
    {
        _employeeRepository.Create(new Employee {Id = 1, Name = "Test Employee"});
        _customerRepository.Create(new Customer { Id = 2, Name = "Test Customer" });

        _uow.SaveChanges();
    }

}

回答1:

I think what you are looking for is a per request lifetime manager so that you only get one UnitOfWork instance and one DbContext instance for the duration of a request. Unity 3 has the Unity bootstrapper for ASP.NET MVC which has a PerRequestLifetimeManager which lets you do this.

If you are not using ASP.NET then you could probably use a PerResolveLifetimeManager. Another approach I've seen is a HierarchicalLifetimeManager combined with a child container (which makes the registrations a singleton within the child container).