Reputation: 107
I am just starting in DDD and have a question regarding interfaces of objects and repositories. Suppose I have the following objects
public interface IPerson { ... }
public class Student
{
double gpa;
...
}
public class Teacher
{
double salary; ...
}
then I also have two repositories such as
public class StudentRepository :IRepository { public void Save(Student) }
public class TeacherRepository :IRepository { public void Save(Teacher) }
My question is, suppose I have a list of IPerson objects called persons, is there a way where I can just do something like repository.Save(persons) ? Without having to use reflection to figure out what type the IPerson actually is.
I currently have another class
PersonRepository :IRepository
{
public void Save(IPerson person)
{
if(Person is Student)
{
new StudentRepository.Save(person as Student);
}
else if(Person is Teacher)
{ ....}
}
}
Then I can call personRepository.Save(persons); However this doesnt feel like an optimal way to structure things. How can I improve this design?
Thanks
EDIT:
What I'm looking for is, say I receive an IPerson object called person. I do not necessarily know what implementation it is, I just want to call repository.Save(person) and have it call the correct repository. Is there a way to do this without using some sort of switch statement with reflection?
Upvotes: 3
Views: 299
Reputation: 35925
Consider using generic repository
class Repository<T> :IRepository<T>
{
public void Save(T entity)
{
...
}
}
Usage
IRepository<Student> repo1 = new Repository<Student>();
repo1.Save(new Student());
IRepository<Teacher> repo2 = new Repository<Teacher>();
repo2.Save(new Teacher());
At the top level, say in the main method or global.asax
IRepository<Student> studentRepo = IoC.Current.Resolve<IRepository<Student>>();
Later in a class that needs to save data, pass IRepository<Student> studentRepo
into constructor
class Foo
{
private IRepository<Student> repo
Foo(IRepository<Student> repo)
{
this.repo = repo;
}
public void Save(Student s)
{
repo.Save(s);
}
}
You can move a save operation to the IPerson<T>
class Person<T> : IPerson<T>
{
private IRepository<T> repo;
Person(IRepository<T> repo)
{
this.repo = repo;
}
public void Save()
{
repo.Save<T>();
}
}
So when you derive Teacher
and Student
from Person<T>
you pass correspondent T
, like
class Student : Person<Student>
{
private IRepository<Student> repo;
Person(IRepository<Student> repo):base(repo)
{
...
}
}
This shall give you the ability to work with List without Reflection or switch kung fu.
Upvotes: 4
Reputation: 17350
You can potentially have a method with C# generics
interface Repository<TEntity> where TEntity : class {
void Save(TEntity entity);
}
But I would discourage having generic (as in generalized, not C# generics) repositories. Repository interface should be domain driven and specific to your entity. Please consider this article by Greg Young.
It is also not clear why you have interfaces for you entities (IPerson
). Interfaces are usually created at the seam of the application. Are you planning to have more than one implementation of IPerson?
Upvotes: 3
Reputation: 48279
Two possible approaches.
First, interfaces specific for domain types
interface IStudentRepository
interface ITeacherRepository
class StudentRepository : IStudentRepository
class TeacherRepository : ITeacherRepository
Second, a generic interface
interface IRepository<T>
class StudentRepository : IRepository<Student>
class TeacherRepository : IRepository<Teacher>
Upvotes: 1