1

Consider the following classes and interfaces:

interface INameable
{
    string Name { get; }
}

interface IRepository<T>
{
    void Add(T obj);
    IEnumerable<T> Values { get; }
}

class Person : INameable
{
    public string Name { get; set; }
    public int Age { get; set; }
}

class Car : INameable
{
    public string Name { get; set; }
    public string Model { get; set; }
}

I now would like to create a Repository class that implements both IRepository<Car> and IRepository<Person>. Here is a sample implementation:

class Repository : IRepository<Car>, IRepository<Person>
{
    Dictionary<string, object> values = new Dictionary<string, object>();

    void AddValue(INameable o)
    {
        values.Add(o.Name, o);
    }

    IEnumerable<T> ValuesOfType<T>() 
    {
        return values.Values.OfType<T>();
    }

    void IRepository<Car>.Add(Car obj)
    {
        AddValue(obj);
    }

    void IRepository<Person>.Add(Person obj)
    {
        AddValue(obj);
    }

    IEnumerable<Car> IRepository<Car>.Values
    {
        get { return ValuesOfType<Car>(); }
    }


    IEnumerable<Person> IRepository<Person>.Values
    {
        get { return ValuesOfType<Person>(); }
    }
}

This works exactly as expected. However, it is very repetitive; the code for the implementation of IRepository<Person> and IRepository<Car> is nearly exactly the same.

What I would like to do is implement IRepository for all T where T is a INameable. I tried this:

class Repository2 : IRepository<Car>, IRepository<Person>
{
    // same as before
    Dictionary<string, object> values = new Dictionary<string, object>();

    void AddValue(INameable o)
    {
        values.Add(o.Name, o);
    }

    IEnumerable<T> ValuesOfType<T>() 
    {
        return values.Values.OfType<T>();
    }


    // using generics to implement both the interfaces
    void Add<T>(T obj) where T : INameable
    {
        AddValue(obj);
    }

    void Values<T>() where T : INameable
    {
        return ValuesOfType<T>();
    }

}

However I get errors like:

ConsoleApp.Repository2' does not implement interface member 'ConsoleApp.IRepository<ConsoleApp.Car>.Add(ConsoleApp.Car)'

I'm not sure why the Add<T> and Vales<T> methods aren't being matched - both T's can be set to Person and Car, and then they would exactly match the method type needed.

Finally, I tried:

class Repository3 : IRepository<T> where T is INameable {... }

However, I get an error "Constraints are not allowed on non-generic declarations".

What is the best way of solving this problem?

Note that I am doing this as a method to simply access to a DbContext class (which has references to every table in the application), so instead of passing the full database to each controller, I only pass the data that is needed. I was doing this to better separate the database from the rest of the app, and to improve testability. If there is a better way of doing this that could also help.

2
  • Well, obviously generic methods in C# are not designed to allow for wildcard placeholders of multiple interface methods resolving to the same signature in the unique scenario where you want the implementation for all those methods to be identical. You'll have to use dynamic proxies or some such if you want to avoid the repetition. But really -- is it worth it? There's something to be said for the clarity provided by the more verbose example, IMO. Commented Apr 24, 2014 at 6:21
  • If it isn't possible to improve my first example (without making it very complex/obscure), then I don't mind sticking with it. It is just I am planning on doing this for another 27 entities, and that is a lot of pretty repetitive code. Commented Apr 24, 2014 at 6:24

3 Answers 3

1

You could have an abstract class with the implementation and then just inherit it for specific types.

public interface INameable
{
    string Name { get; }
}

public interface IRepository<T>
{
    void Add( T obj );
    IEnumerable<T> Values { get; }
}

public class Person : INameable
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Car : INameable
{
    public string Name { get; set; }
    public string Model { get; set; }
}

public abstract class AbstractRepository<T> : IRepository<T>
    where T : INameable
{
    // same as before
    Dictionary<string, object> values = new Dictionary<string, object>();

    void AddValue( INameable o )
    {
        values.Add( o.Name, o );
    }

    IEnumerable<T> ValuesOfType<T>()
    {
        return values.Values.OfType<T>();
    }


    // using generics to implement both the interfaces
    public void Add( T obj ) 
    {
        AddValue( obj );
    }

    public IEnumerable<T> Values 
    {
        get
        {
            return ValuesOfType<T>();
        }
    }
}


public class CarRepository : AbstractRepository<Car> { }

public class PersonRepository : AbstractRepository<Person> { }
Sign up to request clarification or add additional context in comments.

2 Comments

That is a nice solution. I'll give this a go and see how it works out.
Thanks, that solution worked well. I ended up altering it bit by using composition rather than inheritance to access the base repository type, but it still had the same effect.
1

In my experience, it is much easier that all the entities, that are to be added to some repository, conform to some interface, say IBaseObject:

interface IRepository
{
    void Add(IBaseObject obj);
    IEnumerable<IBaseObject> Values { get; }
}

This generally ends up being a good solution because in IBaseObject you could have an identifier so you know when to add or an update an existing record.

update:

Another approach is to use the following pattern, again still relying on IBaseObject:

interface IRepository
{
   void Add(T obj) where T : IBaseObject;
   IEnumerable<T> GetValues() where T : IBaseObject;
}

4 Comments

The problem I had with this solution is it loses a bit of type safety - the Values method will need to be cast to a IEnumerable<Car> for example before being used. It also doesn't return a separate list of objects depending on what type you want. I would prefer the first version to this, even if it is a lot more code.
I see your dilemma. There's an alternative pattern, I updated the answer.
The second solution could also work. I'll need a little while to see how it works out.
@Candide: he has such base interface, INameable.
0

I think, you should create class that creates repositories. Something like this:

    class Repository<T> : IRepository<T>
    where T : INameable
{
    Dictionary<string, T> values = new Dictionary<string, T>();

    void AddValue(T o)
    {
        values.Add(o.Name, o);
    }

    public void Add(T obj)
    {
        AddValue(obj);
    }

    public IEnumerable<T> Values
    {
        get { return values.Values; }
    }
}

class UnitOfWork
{
    private readonly Dictionary<Type, object> _repositories = new Dictionary<Type, object>();

    public IRepository<T> GetRepository<T>()
        where T : INameable
    {
        object repository;
        if (!_repositories.TryGetValue(typeof (T), out repository))
        {
            repository = new Repository<T>();
            _repositories[typeof (T)] = repository;
        }
        return (IRepository<T>)repository;
    }
}

And use it like that:

        var unitOfWork = new UnitOfWork();

        unitOfWork.GetRepository<Car>().Add(new Car {Name = "Audi"});
        unitOfWork.GetRepository<Car>().Add(new Car { Name = "BMW" });

        foreach (var car in unitOfWork.GetRepository<Car>().Values)
            Console.WriteLine(car.Name);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.