Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Of Work and Repository inter dependency

I have seen lots of posts (and debates!) about which way round UnitOfWork and Repository. One of the repository patterns I favor is the typed generic repository pattern, but I fear this had lead to some issues with clean code and testability. Take the following repository interface and generic class:

public interface IDataEntityRepository<T> : IDisposable where T : IDataEntity
{
   // CRUD
   int Create(T createObject);
   // etc.
}


public class DataEntityRepository<T> : IDataEntityRepository<T> where T : class, IDataEntity
{
   private IDbContext Context { get; set; }

   public DataEntityRepository (IDbContext context)
   {
     Context = context;
   }

   private IDbSet<T> DbSet { get { return Context.Set<T>(); } }   

   public int Create(T CreateObject)
   {
      DbSet.Add(createObject);
   }

   // etc.

}

// where

public interface IDbContext
{
   IDbSet<T> Set<T>() where T : class;
   DbEntityEntry<T> Entry<T>(T readObject) where T : class;

   int SaveChanges();
   void Dispose();
}

So basically I am using the Context property in each pattern to gain access to the underlying context. My problem is now this: when I create my unit of work, it will effectively be a wrapper of the context I need the repository to know about. So, if I have a Unit Of Work that declares the following:

public UserUnitOfWork(
    IDataEntityRepository<User> userRepository,
    IDataEntityRepository<Role> roleRepository)
{
    _userRepository = userRepository;
    _roleRepository = roleRepository;
}

private readonly IDataEntityRepository<User> _userRepository;

public IDataEntityRepository<User> UserRepository
{
    get { return _userRepository; }
}

private readonly IDataEntityRepository<Role> _roleRepository;

public IDataEntityRepository<Role> RoleRepository
{
    get { return _roleRepository; }
}

I have a problem with the fact that the two repositories I am passing in both need to be instantiated with the very Unit Of Work into which they are being passed. Obviously I could instantiate the repositories inside the constructor and pass in the "this" but that tightly couples my unit of work to a particular concrete instance of the repositories and makes unit testing that much harder. I would be interested to know if anyone else has headed down this path and hit the same wall. Both these patterns are new to me so I could well be doing something fundamentally wrong. Any ideas would be much appreciated!

UPDATE (response to @MikeSW)

Hi Mike, many thanks for your input. I am working with EF Code First but I wanted to abstract certain elements so I could switch to a different data source or ORM if required and because I am (trying!) to push myself down a TDD route and using Mocking and IOC. I think I have realised the hard way that certain elements cannot be unit tested in a pure sense but can have integration tests! I'd like to raise your point about Repositories working with business objects or viewmodels etc. Perhaps I have misunderstood but if I have what I see as my core business objects (POCOs), and I then want to use an ORM such as EF code first to wrap around those entities in order to create, and then interact with, the database (and, it's possible, I may re-use these entities within a ViewModel), I would expect a Repository to handle these entities directly in the context of some set of CRUD operations. The entities know nothing about the persistence layer at all, neither would any ViewModel. My unit of work simply instantiates and holds the required repositories allowing a transaction commit to be performed (across multiple repositories but the same context/ session). What I have done in my solution is to remove the injection of an IDataEntityRepository ... etc. from the UnitOfWork constructor as this is a concrete class that must know about one and only one type of IDataEntityRepository it should be creating (in this case DataEntityRepository, which really should be bettered names as EFDataEntityRepository). I cannot unit test this per se because the whole unit logic would be to establish the repositories with a context (itself) to some database. It simply needs an integration test. Hope that makes sense?!

like image 889
Benjamin Jones Avatar asked Feb 18 '23 22:02

Benjamin Jones


1 Answers

To avoid dependency on each repository in your Unit of Work, you could use a provider based on this contract:

public interface IRepositoryProvider
{
    DbContext DbContext { get; set; }
    IRepository<T> GetRepositoryForEntityType<T>() where T : class;
    T GetRepository<T>(Func<DbContext, object> factory = null) where T : class;
    void SetRepository<T>(T repository);
}

then you could inject it into your UoW that would look like this:

public class UserUnitOfWork: IUserUnitOfWork
{
    public UserUnitOfWork(IRepositoryProvider repositoryProvider)
    {
        RepositoryProvider = repositoryProvider;
    }

    protected IDataEntityRepository<T> GetRepo<T>() where T : class
    {
        return RepositoryProvider.GetRepositoryForEntityType<T>();
    }

    public IDataEntityRepository<User> Users { get { return GetRepo<User>(); } }        
    public IDataEntityRepository<Role> Roles { get { return GetRepo<Role>(); } }        
...
like image 134
g1ga Avatar answered Mar 28 '23 14:03

g1ga