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.