问题
I are planning migrate our data access layer to using repository pattern and unit of work.
I do know repository will help me to change persistence store (database, collection...etc) and technology such as EF to MongoDB easily. So I noticed some key points of implementation of a repository such as:
- Return
IEnumerableinstead ofIQueryable - Repository should take responsibilities for CRUD operations only
- Return type of repository method should be model (entity)
- Only implement repository for aggregate root
If I apply these key points during implement repository in my project, I totally lost how to deal with complex query in which related to multiple entities.
Currently what I already had was that on BLL library with a lot of services class will contact directly to DbContext and DbSet of EF and some of validation like this:
public IEnumerable<ProjectDTO> GetProjectWithDetails()
{
// Validation
// Logging
// Can be any logic need to before query data.
Dbcontext.Projects.Where(p =>
// multiple of conditions go here follow business rules
// conditions will need to check another entities (task, phase, employee...) such as:
// 1. project have task status 'in-progress' .. etc
// 2. project have employeeid 1,2,3..
// 3. project have stask start at some specific date.
// 4....
)
.Select(p => new ProjectDTO
{
Label = p.Label,
Phase = new PhaseDTO{
Label = p.Phase.Label,
Tasks = p.Phase.Tasks.Select(t => new TaskDTO{
// some related properties
})
}
}).ToList();
}
I am currently using Data Transfer Object (DTO) to be the middle classes between model and viewmodel on controller and using Mapper to map properties.
If I keep key notes on repository above I need to do multiple round trip to database for getting data, and it will return whole model instead of useful columns. But if I migrate those kind of methods to repository I will broken repository pattern because it will contain business logic and return type not an model.
So question is what should I do in this case? Please give me some advise to put me on the right track.
Many thanks.
回答1:
This depends on opinion and the use case, but I personally do not agree with some of the key points you mentioned.
Return IEnumerable instead of IQueryable
Agree. Returning IQueryable defeats basic purpose of existence of Repository. There are lot many articles on net explaining how this creates more problem than a solution. Though, I have learned to never say never. Refer this, this, or this. Or simply search google.
Repository should take responsibilities for CRUD operations only
Agree. With simple CRUD, it may also do complex reads and writes. My experience tell that in exceptional cases, you have to put a part of business logic in repository if you want to implement it on RDBMS side. This is not right or wrong. If you know what you are doing, there should not be an issue.
Return type of repository method should be model (entity)
If you are not using DDD, then yes. Otherwise, it is implementation decision. With full ORM like EF or NHibernate, it is better to return Domain Model directly instead of per table Entity instance.
It is always suggested that Repository should return Domain Model. That way, mapping of data returned from RDBMS with the Domain Model (and vice versa) becomes responsibility of repository. This avoids necessity of leaking the persistence concerns outside the repository and thus makes your rest of the application persistence ignorant.
But, not every application implement DDD. Many small applications design entities those are mapped 1 to 1 with their database design. In this case, repository may return entity (which is equivalent to your table and fields) itself and mapping becomes responsibility of calling code. Or, repository may map the necessary model and return the model itself. This is strongly discouraged because the problems stated above. With this, you have to give up some of the features full ORMs provide.
All this depends on what is your problem, what are your design objectives, size of application and other design patterns implemented etc. That is why it becomes design decision.
Only implement repository for aggregate root
Agreed if it is with DDD. If not, multiple choices are available like per table repository. Again, depends on use case.
About complex query
It is not necessary that repositories should only implement simple CRUD methods. It may also return complex object graph. It may do complex querying. That said, with simple methods like Get, GetById etc, it may also consume complex methods like GetTopBrokenVehicles(vehicleType, top). It is absolutely fine if you write separate method for complex query.
Challenge is that, how you accept the necessary parameters. You may accept the parameters inline or build separate simple input parameter class.
Here is sample code for Repository and UoW.
回答2:
One of benefit of using Repository Pattern is hiding complex queries, you should see repository as collection of objects in memory (martin fowler):
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection. Client objects construct query specifications declaratively and submit them to Repository for satisfaction. Objects can be added to and removed from the Repository, as they can from a simple collection of objects, and the mapping code encapsulated by the Repository will carry out the appropriate operations behind the scenes. Conceptually, a Repository encapsulates the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer. Repository also supports the objective of achieving a clean separation and one-way dependency between the domain and data mapping layers https://martinfowler.com/eaaCatalog/repository.html
来源:https://stackoverflow.com/questions/50312714/should-write-complex-query-in-repository-or-service-layer