Reputation: 23
I have the following classes in c#:
public class Customer{
public long Id { get; set;}
public String Firstname { get; set;}
public String Lastname { get; set;}
public Customer(long id, String firstname, String lastname){...}
}
public class Book{
public long Id { get; set;}
public String Title { get; set;}
public String Author{ get; set;}
public int NumberOfCopies{ get; set;}
public Book(long id, String title, String author, int numberofcopies){...}
}
My repository is generic and the interface is as follows:
public interface IGenericRepository<T> where T : class, new(){
Add(T entity);
Update(T entity);
Delete(T entity);
GetAll();
}
So to create a repository for a Customer I would create a IGenericRepository<Customer> and for a Book IGenericRepository<Book>. To access these methods from the GUI, I would need a method like: AddCustomer(long id, String firstname, String lastname) and the same for a book, because the GUI doesn't know the entities themselves. I was thinking to create a CustomerService and BookService which hold these methods. But then I would have to create a new Service for every entity I add.
My question is, how can I make this service generic and still keep the parameters from the constructor of the entity?
To be clear, I would like a generic Service class, which can add both books and customers, but with the same method. So for example:
Add(<constructor parameters of T>)
This method will be called from a Controller class which will have all of the Service. The GUI can then access these service methods through the controller.
Is this possible? If yes, how? If no, is there a better solution to achieve this?
Thanks in advance!
Upvotes: 2
Views: 2165
Reputation: 100547
Pretty standard approach is to pass "creator" delegate to your repository, also I'm not sure if it fits your goals:
public interface IGenericRepository<T> where T : class{
Add(Func<T> creator);
...
}
Repository<T> : IGenericRepository<T> where T : class
{
public Add(Func<T> creator)
{
T newOne = creator();
....
}
}
// usage
bookRepository.Add(() => new Book(42, "some title", ...));
Upvotes: 0
Reputation: 532
They way I have typically seen it done is that you have the following:
1. An interface that defines generic Add, Update, Delete, etc:
public interface IGenericRepository<T> where T : class
{
Add(T entity);
Update(T entity);
Delete(T entity);
GetAll();
}
2. A generic repository that accepts the different types you are going to be persisting to your data store.
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
private connectionInformation;
public GenericRepository<T>(object connectionInformation)
{
// do something with the connection info, dbContext, etc...
}
public T Add(T entity)
{
// implementation...
}
public T Update(T entity)
{
// implementation...
}
public T Delete(T entity)
{
// implementation...
}
public List<T> GetAll()
{
// implementation...
}
}
3. A unit of work that sets up instances of your generic repositories for the different concrete types.
public class UnitOfWork
{
private object connectionInformation;
public UnitOfWork(object connectionInformation)
{
// set up your connection information
this.connectionInformation = connectionInformation;
this.CustomerRepository = new GenericRepository<Customer>(connectionInformation);
this.BookRepository = new GenericRepository<Book>(connectionInformation);
}
public GenericRepository<Book> BookRepository { get; private set; }
public GenericRepository<Customer> CustomerRepository { get; private set; }
}
4. A service/services layer that can instantiate a "unit of work" from which you can interact with. It is the service that would be responsible for working with the different properties of each type, but the data saving and retrieving would be handled via the unitOfWork. In your service, you could have a method like this:
public void DeleteFirstBook()
{
var unitOfWork = new UnitOfWork(connnectionInformation);
var books = unitOfWork.BookRepository.GetAll();
if(books.Any())
{
unitOfWork.BookRepository.Delete(books.First());
}
}
You could also do the same for Customers. Having the service layer in place helps your code from being too tightly coupled. The "Data Layer" should only be responsible for interacting with the database (Create, Read, Update, Delete). As you stated, your UI layer should also not know how to create new objects, or interact with the database. So the service becomes important because it knows how to set up data, where to put the data, and how to return it back to the UI.
Hope it helps!
Upvotes: 0
Reputation: 51
I would change the IRepository<t>
method Add(T entity)
to Add(object entity)
. That way you can use the exact same code for any entity.
Upvotes: 0
Reputation: 6795
One option is to accept the parameters as desired, e. g.:
public void Add<T>(params object param) { ... }
T
is of the type which you want to create. Via reflection you check which constructors are available, chose the one that fits the list of parameters. Then instantiate the object. Voila.
Upvotes: 1