代码优先 - 存储库模式 - 设计注意事项

Code First - Repository Pattern - Design Considerations

我正在尝试制作一个 Repository+UnitOfWork 平台。我的 IRepository 界面如下所示。

 public interface IRepository<TEntity>
        where TEntity : IEntity
    {
        IQueryable<TEntity> GetAll();
        TEntity Get(int id);
        IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> predicate);
        TEntity Add(TEntity entity);
        TEntity Delete(TEntity entity);
        void Save(TEntity entity);
    }

我对以下一些问题感到困惑...

  1. 在界面中处理的 IEntity 是代码优先实体(不是业务对象)。理想情况下,存储库应该处理 (accept+return) 它们还是业务对象本身?
  2. 存储库应该 return IQueryable 还是 IEnumerable?
  3. 如果问题 #1 的答案是业务对象,那么如何 实现实体和业务对象之间的映射?
  4. 如果我引入一个新的服务层(这将是 由客户端消费),服务层将是调用存储库方法和映射的一次。这是正确的吗?
  5. 如果以上问题的答案是肯定的,那么在做映射 服务层打破了单一职责原则。如何获得 结束了吗?

我也欢迎 link 实现了存储库和 UnitOfWork 的示例应用程序(MVC、EF、SQL 服务器)。

软件设计没有对错之分,但据我所知,有一些设计实践可以回答您的问题。

The IEntity which are handled in the interface are code first entities (not business objects). Is ideally a Repository expected to handle (accept+return) them or the business objects itself?

  • 理想情况下,存储库层应该只处理实体对象。不要用业务对象和映射逻辑淹没它

Should a Repository return IQueryable or IEnumerable?

  • 我更愿意用Iqueryable,因为Iqueryable也继承自IEnumerable,所以IEnumerable能做的事,IQueryable也能做.更重要的是,每个使用 Iqueryable 的查询尝试都将在 database 层中执行。所以总而言之,如果您不确定是否还需要执行任何额外的查询(例如:过滤数据),IQueryable 绝对是必须的。

If the answer for the Question #1 is business object, then how to implement mapping between Entities and Business objects?

  • 有很多方法可以进行映射,AutoMapper 是用于此目的的良好且稳定的库。

If I introduce a new service layer (which will be consumed by the client), the service layer will be the once which calls the Repository methods, and mapping. Is this correct?

  • 这实际上取决于您如何设计 服务层。通常的流程是:

    调用Repository方法=>获取数据=>映射到业务对象=>执行额外的逻辑=>return业务对象

If the answer to the above question is yes, then doing mapping in service layer breaks Single Responsibility Principle. How to get over it?

正如单一职责原则的定义(一个class应该只有一个改变的理由),你的服务层职责是在业务对象,所以我会说它不会违反 SRP。

public SampleDTO SampleServiceMethod(InputModel input)
{
    var model = _sampleRepository.FindBy(input.Id);

    var dto = SampleMapper.ToSampleDto(model);

    // do something and return the dto
    dto.Test = 1;

    return dto;
}
  1. The IEntity which are handled in the interface are code first entities (not business objects). Is ideally a Repository expected to handle (accept+return) them or the business objects itself?

您是否有任何理由拥有单独的域模型(业务对象)和持久性模型(代码优先实体)? EF 对代码优先没有太多额外要求类,通常你可以直接持久化你的业务对象。

如果您选择有一个单独的持久性模型,那么它应该是存储库的实现细节。存储库应与业务对象一起使用。

  1. Should a Repository return IQueryable or IEnumerable?

视情况而定。 IQueryable 无需修改存储库即可轻松在应用层创建查询。当您有一个单独的简单读取堆栈时,它在 CQRS 中特别有用。

您必须知道,使用 IQueryable 会引入对基础数据源的依赖。当您更改数据源时,适用于一个数据源的查询可能会在运行时失败。

  1. If the answer for the Question #1 is business object, then how to implement mapping between Entities and Business objects?

您可以使用像 Automapper 这样的工具。

  1. If I introduce a new service layer (which will be consumed by the client), the service layer will be the once which calls the Repository methods, and mapping. Is this correct?

服务层将调用存储库。存储库将 return/add/modify 一个业务对象。任何映射(如有必要)都将在存储库中完成。