UnitOfWork & Generic Repository, SOLID principles with Custom Repository

Viewed 516

I'm using UnitOfWork and Repository patterns in my project. I'm trying to code clean.

This is my IUnitOfWork.cs (Application Layer)

public interface IUnitOfWork : IDisposable
{
    int Save();
    IGenericRepository<TEntity> Repository<TEntity>() where TEntity : class;
}

The implementation UnitOfWork.cs : (Persistence Layer)

public class UnitOfWork : IUnitOfWork
{      
    private readonly DBContext _context;
    private Hashtable _repositories;
    public UnitOfWork(DBContext context)
    {
        _context = context;
    }

    public IGenericRepository<T> Repository<T>() where T : class
    {
        if (_repositories == null)
            _repositories = new Hashtable();

        var type = typeof(T).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(GenericRepository<>);

            var repositoryInstance =
                Activator.CreateInstance(repositoryType
                    .MakeGenericType(typeof(T)), _context);

            _repositories.Add(type, repositoryInstance);
        }

        return (IGenericRepository<T>)_repositories[type];
    }

    public int Save()
    {
        // Save changes with the default options
        return _context.SaveChanges();
    }

    // etc.. Dispose()
}

My IGenericRepository.cs : (Application Layer)

public interface IGenericRepository<TEntity>
    where TEntity : class
{
    void Update(TEntity entity);
    void Delete(object id);
    void InsertList(IEnumerable<TEntity> entities);
    // etc..
}

In my service : (Application Layer)

var result = UnitOfWork.Repository<Entities.Example>().Delete(id);

And using Unity, I inject the dependency in the container.

  container.RegisterType<IUnitOfWork, UnitOfWork>(new HierarchicalLifetimeManager())

And it works like a charm.

Now I have a custom Repository ICustomRepository:

public interface ICustomRepository: IGenericRepository<Entities.Custom>
{
    void Test();
}

How can I access the Test() function using my IUnitOfWork?

var result = UnitOfWork.Repository<Entities.Custom>().Test();  // not working

UPDATE:

@Thomas Cook give me a way using cast :

   (UnitOfWork.Repository<Entities.Custom>() as ICustomRepository).Test();

I get a NullReferenceException:

System.NullReferenceException: 'Object reference not set to an instance of an object.'
2 Answers

You'll have to cast, because UnitOfWork Repository method returns a IGenericRepository which doesn't declare Test. So you'll need to cast the returned value to a ICustomRepository which inherits IGenericRepository and bolts on the Test method.

Whilst casting would work (if the repository wouldn't be null) you could ask yourself is this is useful; What good is an abstraction of you're going to depend on knowing its details, i.e.: the caller now knows it actually is a non-generic interface and type, namely a ICustomRepository (btw the reason you get the null ref probably is because UnitOfWork only creates the generic repositories and your custom repo doesn't exist).

A better (IMO) and more explicit way to design your Unit of Work not in a generic way but list all repositories:

public interface IUnitOfWork : IDisposable
{
    int Save();
    ICustomRepository CustomRepository {get;}
    IGenericRepository<Entities.Example> ExampleRepository {get;}
    // etc...
}

This way you don't need any casting or know its details.

Furthermore, I would recommend to pass all repositories to the constructor of the UnitOfWork from your dependency injection and don't make its responsibility too big.

public UnitOfWork(DBContext context, ICustomRepository customRepository ...)
    {
       //
    }

Just make sure you use the same instance of DbContext in your Unit of Work as the one that's injected into your repositories.

Related