简体   繁体   中英

How can I decouple the EF POCO from the generic IRepository in this pattern?

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 :

EmployeeDTO FindEmployeeByName(string name)
{
    return Mapper.Map<EmployeeDTO>(dbSet.Where(o=>o.name.Contains(name)).FirstOfDefault());
}

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.

Another way to do this is to make your data layer depend on the business layer and have your repository "project" entities into your business objects (DTO's). This way your BL is at the bottom and UI and Data depend on it, but UI and Data do not depend on each other.

This is the method espoused by Mark Seemann in his book on Dependency Injection.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM