实体框架vs存储库模式

本文关键字:模式 存储 vs 框架 实体 | 更新日期: 2023-09-27 18:08:47

我想知道使用实体框架的项目中存储库模式的有用性。对此有相反的说法——有些人说EF是存储库和工作单元模式本身的实现,所以没有必要将其包装在下一个抽象层中,有些人认为它具有诸如DAL和BL分离以及更容易创建单元测试等优点。根据我的经验,我经常遇到以下方法(通常,不仅仅是在EF项目中):

Repository (DAL) <-> Service (BL) <-> Controller
Repository + Service + Type = Model

存储库具有仅负责数据访问的方法,即:

public interface IUsersRepository
{
  IEnumerable<User> GetAll();
  User Get(int id);
  User GetByLogin(string login);
  void Update(User item);
  void Delete(int id);
  void Delete(User item);
  // ...
}

通常使用的是通用存储库,它的方法接收过滤、排序等函数。

服务反过来使用存储库并包含业务逻辑,即:

public interface IUsersService
{
  // ...
  bool VerifyPassword(string login, string password);
  void ChangePassword(string login, string password);
  // ...
}

据我所知,服务不应该有任何DAL操作——这意味着我们不应该从存储库返回iquerable集合,因为这样查询将在存储库外执行,单元测试将不是完全可靠的(linq到实体和linq到对象之间的区别)。

我在这里看到了查询效率的一个问题——我们经常会从数据库中获得比需要更多的数据,然后在内存中过滤它。

例如,我们考虑bool VerifyPassword(string login, string password)方法。

在这种情况下,我们需要从数据库中获得整个User实体,例如,它可以有50个属性,仅用于密码验证目的。当然,我们可以在存储库中创建许多方法,例如:

string GetPasswordHash(string login) 

bool VerifyPassword(string login, string passwordHash) 
{
  return db.Users.Any(x => x.Login = login && x.Password = passwordHash);
} 

不需要从数据库中获取整个实体,但在我看来,这可能是一个"小"的开销。

我们也可以将VerifyPassword函数从服务移到存储库——然后我们应该问自己一个问题,是否需要两层——存储库和服务。但是如果我们合并它们,我们将失去分离DAL和BL层的好处——单元测试实际上是集成测试。所以也许它会更简单(和更好)保持在控制器和注入模拟DbContext或使用类似的努力单元测试?

我很感激你的回答你如何看待这个问题,以及你如何在你的项目中解决这个问题。

我理解存储库模式允许轻松地将数据源/提供者更改为其他数据源,如nHibernate, LINQ-to-SQL, plain ADO等。

但是你能告诉我你如何在你的存储库API中实现排序和分页吗?我的意思是,将排序和分页规范传递给存储库的最佳方式是什么?我看到一些传递函数谓词的LINQ,但这将紧密耦合库与LINQ -使用它与普通的ADO,存储过程等将是有问题的。创建许多方法,如GetUsersOrderedByNameDesc()等,在我看来是疯狂的。因此,也许可以创建您自己的类来规范,伪造和传递排序/分页标准,并在存储库中处理它?如果是,你能提供一些实现的例子吗?

实体框架vs存储库模式

我将试着回答你的核心问题。这是我的两分钱。为什么是存储库vs实体框架。在设计或选择哪一个时,你需要回答我建议你问的一些问题:

  1. 你的项目是否有可能有不同的存储库?如果是,那么存储库模式优于实体框架是有意义的。
  2. 拥有一个存储库模式还将允许您更容易地设置您的测试框架,在那里您可以拥有存储库模式(DAL)将使用的模型数据库。
  3. 您可能还需要考虑到对数据模型的更改可以渗透回实体框架的业务逻辑更改。