Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics problem with Unit of Work Pattern

I need some help with the design of the Unit of Work + Repository + IoC pattern. I have several interfaces defined as follows:

public interface IRepository<T>
{       
    T GetEntity(int id);
}

public interface IUserRepository : IRepository<User>
{   
    User GetUserByXyz(int id);
}

public interface IUnitOfWork
{
    T Respository<T>() where T : IRepository<T>;
}

I am using Unity to resolve some references. Here's the implementation of the UoW:

public class UnitOfWork : IUnitOfWork
{
    public T Respository<T>() where T : IRepository<T>
    {
        var container = new UnityContainer();
        return container.Resolve<T>();
    }
}

Now i am having trouble calling the interface:

User user = _unitOfWork.Respository<IUserRepository>().GetUserByXyz(1);

The type 'IUserRepository' cannot be used as type parameter 'T' in the generic type or method 'IUnitOfWork.Respository()'. There is no implicit reference conversion from 'IUserRepository' to 'IRepository'.

How do get around the generic constraint error?

like image 915
Fixer Avatar asked Apr 19 '26 01:04

Fixer


2 Answers

Expanding on my comment:

The statement public T Respository<T>() where T : IRepository<T> implies that you're expecting a type that is a Repository of itself, e.g. IUserRepository would have to be an IRepository<IUserRepository> to satisfy your condition.

You need two different generics, one for the item that is held in the reporsitory TItem and another for the repository itself, TRepo.

Then the whole code becomes:

public interface IRepository<TItem>
{       
    TItem GetEntity(int id);
}

public interface IUserRepository : IRepository<User>
{   
}

public interface IUnitOfWork
{
    TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItm>;
}

and

public class UnitOfWork : IUnitOfWork
{
    public TRepo Respository<TRepo,TItem>() where TRepo : IRepository<TItem>
    {
        var container = new UnityContainer();
        return container.Resolve<TRepo>();
    }
}

finally, the call becomes:

User user = _unitOfWork.Respository<IUserRepository,User>().GetEntity(1);
like image 181
Jon Egerton Avatar answered Apr 20 '26 14:04

Jon Egerton


Initial note:

_unitOfWork.Respository<IUserRepository>()…

As it is, you're essentially "abusing" UnityOfWork as a service locator (you can ask it for any type of repository), but it doesn't seem to offer any additional benefits. Is this really what you want? Couldn't you just do away with UnitOfWork and do the following instead:

_unityContainer.Resolve<IUserRepository>()…

Alternative solution that does not require a second type parameter:

I agree with @Jon Egerton that for this to work correctly, one option would be to introduce a second generic type parameter (TItem next to TItemRepository). There is, however, another solution involving a marker interface IRepository:

// non-generic marker interface (empty)
public interface IRepository {}

public interface IRepository<T> : IRepository { … /* as before */ }
//                              ^^^^^^^^^^^^^
//                                  added

public class UnitOfWork
{
    public TRepository Get<TRepository>() where TRepository : IRepository
                                       // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                       // this way, no 2nd type parameter is
                                       // needed since the marker interface is
                                       // non-generic.
    { 
        return new UnityContainer().Resolve<TRespository>();
    }
}

As requested: Unit of Work example:

If you follow Martin Fowler's definition for the Unit of Work pattern, you get something rather different from what you've got right now. Rather, a Unit of Work according to his udnerstanding merely keeps track of all changes that have been made to a collection of objects. The idea behind this is that changes aren't persisted (e.g. to a database) one at a time, but all at the same time, when requested through the unit of work object; thus the pattern's name:

class UnitOfWork<T>
{
    // the UnitOfWork object tracks changes to objects of type T:
    private HashSet<T> newItems;
    private HashSet<T> modifiedItems;
    private HashSet<T> removedItems;

    public void Commit()
    {
        // let's say items are persisted to an RDBMS, then:
        // * generate 'DELETE FROM [tableForItemsOfTypeT]' statements
        //   for all items in the 'removedItems' set;
        // * generate 'INSERT INTO [tableForItemsOfTypeT]' statements
        //   for all items in the 'newItems' set;
        // * generate 'UPDATE [tableForItemsOfTypeT]' statements
        //   for all items in the 'modifiedItems' set.
    }
}
like image 37
stakx - no longer contributing Avatar answered Apr 20 '26 14:04

stakx - no longer contributing



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!