I currently have a Repository/UnitOfWork pattern down. However, there is one hard coupling that I am unable to figure out how I can get rid of.
This is an overview of my pattern:
Business Logic Layer
- IRepository
- Used as type contraint
- IRepository< TModel, TDTO >
- Implements IRepository
- General CRUD methods
- IEmployeeRepository< TModel >
- Implements IRepository< TModel, EmployeeDTO >
- Some employee specific methods
- IUnitOfWork
- Getter for repositories
- Save method
- IEntityWithId
- Interface to force (and expose) DTOs and EF POCOs to have a Int32 field called ID
- Used as type contraints
- EmployeeDTO (Mapped with AutoMapper in the implemented EmployeeRepository)
- DTO entity used in the core project and (to come) test project
Data Layer (Injected with Ninject)
- UnitOfWork
- Implementation of IUnitOfWork based on Entity Framework
- EmployeesRepository
- Implementation of IEmployeeRepository< TModel >
- Employee
- EF POCO
Core
- EmployeesController
- Parametered constructer EmployeesController(IUnitOfWork unitOfWork)
- IUnitOfWork is injected with a Ninject module as a UnitOfWork (from Data layer)
Those are the problem methods in my generic IRepository interface.
TDTO Find(Expression<Func<TModel, bool>> filter);
and
IEnumerable<TDTO> FindAll(Expression<Func<TModel, bool>> filter);
As you can see, there is TModel in there, which is used to build up an expression to filter results. I COULD do something like using an expression on the DTO, but that would require a complete list of employees mapped to DTOs (aka it wouldnt generate a SQL filter but it would filter a SELECT * FROM Employee result list). So this isn’t a good option.
The other, more viable but not optimal solution is to use dynamic LINQ. That way, I can pass a simple string in the Find / FindAll methods and get rid of the TModel requirement. However, this would means that refactoring becomes annoying since it fills the code with magic strings.
Just as I posted this, I think I figured out where my problem is.
Find() and FindAll() shouldn’t even exist. I should write more specific methods like FindEmployeeByName(string name) in the IEmployeeRepository, then implement it like so :
Can anyone confirm this is a proper way to do it? Or suggest something even better?
Edit
Also, if I want to keep Find(Expression…) and FindAll(Expression…) methods, I can, but they are just in the Data layer and used by implemented methods to avoid repeated code. But they should not be used in controllers as they requires knowledge of the underlaying data structure, beyond my Business Logic. That said, they can be implemented in a BaseRepository< TModel > (That I already have but did not mention to keep things more simple) and make EmployeesRepository an extention of BaseRepository. That way, every Repository already have those generic-like method that are Model-Aware.
Not sure if I explained that properly. Let me know if it’s unclear and I’ll try to edit this and make it better.