210

在 OOP 设计模式中,存储库模式和服务层有什么区别?

我正在开发一个 ASP.NET MVC 3 应用程序,并试图理解这些设计模式,但我的大脑还没有明白......

4

5 回答 5

352

存储库层为您提供了对数据访问的额外抽象级别。而不是写

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

要从数据库中获取单个项目,请使用存储库接口

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

并打电话Get(id)。存储库层公开了基本的CRUD操作。

服务层暴露业务逻辑,使用存储库。示例服务可能如下所示:

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

虽然List()存储库的方法返回所有用户,ListUsers()但 IUserService 只能返回一个,用户有权访问。

在 ASP.NET MVC + EF + SQL SERVER 中,我有这样的通信流程:

视图 <- 控制器 -> 服务层 -> 存储库层 -> EF -> SQL Server

Service layer -> Repository layer -> EF这部分对模型进行操作。

Views <- Controllers -> Service layer这部分操作在视图模型上。

编辑:

/Orders/ByClient/5 的流程示例(我们希望查看特定客户的订单):

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

这是订单服务的接口:

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

此接口返回视图模型:

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

这是接口实现。它使用模型类和存储库来创建视图模型:

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}
于 2011-02-19T07:16:45.983 回答
47

正如 Carnotaurus 所说,存储库负责将您的数据从存储格式映射到您的业务对象。它应该处理如何从存储读取和写入数据(删除,更新)。

另一方面,服务层的目的是将业务逻辑封装到一个地方,以促进代码重用和关注点分离。在构建 Asp.net MVC 站点时,这对我来说通常意味着我拥有这种结构

[Controller] 调用 [Service(s)] 谁调用 [repository(ies)]

我发现一个有用的原则是将控制器和存储库中的逻辑保持在最低限度。

在控制器中,这是因为它有助于让我保持干爽。我需要在其他地方使用相同的过滤或逻辑是很常见的,如果我把它放在控制器中,我就不能重用它。

在存储库中,这是因为我希望能够在出现更好的东西时替换我的存储(或 ORM)。如果我在存储库中有逻辑,我需要在更改存储库时重写此逻辑。如果我的存储库只返回 IQueryable 并且服务在另一方面进行过滤,我只需要替换映射。

例如,我最近用 EF4 替换了我的几个 Linq-To-Sql 存储库,而我坚持这一原则的那些存储库可以在几分钟内被替换。在我有一些逻辑的地方,这只是几个小时的问题。

于 2011-02-19T07:28:25.040 回答
35

接受的答案(并被投票数百次)有一个重大缺陷。我想在评论中指出这一点,但它会被埋在 30 条评论中,所以在这里指出。

我接手了一个以这种方式构建的企业应用程序,我最初的反应是WTH?服务层中的视图模型?我不想更改约定,因为已经进行了多年的开发,所以我继续返回 ViewModel。天啊,当我们开始使用 WPF 时,它变成了一场噩梦。我们(开发团队)总是说:哪个 ViewModel?真正的(我们为 WPF 编写的)还是服务的?它们是为 Web 应用程序编写的,甚至有IsReadOnly标志来禁用 UI 中的编辑。主要的,主要的缺陷,都是因为一个词:ViewModel

在你犯同样的错误之前,除了我上面的故事之外,还有一些原因:

从服务层返回 ViewModel 是一个巨大的禁忌。这就像在说:

  1. 如果你想使用这些服务,你最好使用 MVVM,这里是你需要使用的 ViewModel。哎哟!

  2. 这些服务假设它们将显示在某处的 UI 中。如果它被 Web 服务或 Windows 服务等非 UI 应用程序使用怎么办?

  3. 那甚至不是真正的 ViewModel。一个真正的 ViewModel 具有可观察性、命令等。那只是一个名字不好的 POCO。(请参阅我上面的故事,了解为什么名称很重要。)

  4. 消费应用程序最好是一个表示层(该层使用 ViewModel),它更好地理解 C#。另一个哎哟!

请不要那样做!

于 2018-05-15T04:13:26.200 回答
8

通常,存储库用作填充实体的脚手架 - 服务层会出去并获取请求。您可能会在服务层下放置一个存储库。

于 2011-02-19T06:59:47.580 回答
6

存储层用于访问数据库并有助于扩展对数据库的 CRUD 操作。而服务层由应用程序的业务逻辑组成,并且可以使用存储库层来实现涉及数据库的某些逻辑。在应用程序中,最好有一个单独的存储库层和服务层。拥有独立的存储库和服务层使代码更加模块化,并将数据库与业务逻辑分离。

于 2018-09-30T15:46:17.047 回答