存储库和服务层之间的区别?

Posted

技术标签:

【中文标题】存储库和服务层之间的区别?【英文标题】:Difference between Repository and Service Layer? 【发布时间】:2011-06-30 06:42:39 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

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

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() 方法返回所有用户,但IUserService 的ListUsers() 只能返回用户有权访问的用户。

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

视图 服务层 -> 存储库层 -> EF -> SQL Server

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

视图服务层这部分操作在视图模型上。

编辑:

/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...     
             
         );
     

【讨论】:

@Sam Striano:正如您在上面看到的,我的 IRepository 返回 IQueryable。这允许在服务层中添加额外的 where 条件和延迟执行,而不是稍后。是的,我使用一个程序集,但所有这些类都放在不同的命名空间中。没有理由在小项目中创建许多程序集。命名空间和文件夹分离效果很好。 为什么要在服务中返回视图模型?如果您有多个客户端(移动/网络),服务不是应该模拟的吗?如果是这样,那么视图模型可能因不同平台而异 同意@Ryan,服务层应该返回实体对象或实体对象的集合(不是IQueryable)。然后在 ui 实体上通过 Automapper 映射到 SomeViewModel。 @Duffp:您不必为每个实体创建存储库。您可以在 IOC 库中使用通用实现并将 IRepository&lt;&gt; 绑定到 GenericRepository&lt;&gt;。这个答案很老了。我认为最好的解决方案是将所有存储库合并到一个名为UnitOfWork 的类中。它应该包含每种类型的存储库和一个名为SaveChanges 的方法。所有存储库都应共享一个 EF 上下文。 而不是从服务层返回视图模型,您应该返回 DTO 并使用自动映射器将其转换为视图模型。有时它们是相同的,当它们不同时,你会庆幸你已经实施 YGTNI “你将需要它”【参考方案2】:

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

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

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

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

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

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

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

【讨论】:

我同意你的观点,Mikael。事实上,我在我的技术博客freecodebase.com 中应用了相同的场景,并且在这个实现中我使用了代码优先的方法。源代码也可以在这里下载。 我一直在研究在现有 MVC 应用程序中应用存储库模式的一般主题。它是一个定制的框架,具有类似 Active Record 的 ORM 和其他 Rails/Laravel 约定,并且在我现在正在做的工作中存在一些架构问题。我遇到的一件事是存储库“不应返回 ViewModel、DTO 或查询对象”,而应返回存储库对象。我正在考虑服务通过onBeforeBuildBrowseQuery 之类的方法与存储库对象交互的位置,并且可以使用查询生成器来更改查询。 @Toffee,你的链接坏了,你能更新一下吗,我需要这个实现的源代码。【参考方案3】:

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

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

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

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

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

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

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

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

请不要那样做!

【讨论】:

我只需要对此发表评论,尽管我知道它不会增加讨论:“这只是一个名字不好的 POCO。” 【参考方案4】:

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

【讨论】:

所以在使用 EF4 的 ASP.NET MVC 应用程序中,可能是这样的:SQL Server --> EF4 --> Repository --> Service Layer --> Model --> Controller 和副反之亦然? 是的,您的存储库可用于从 EF4 获取轻量级实体;并且您的服务层可用于将这些发送回专门的模型管理器(您的场景中的模型)。控制器会调用您的专业模型管理器来执行此操作...快速查看我的 Mvc 2 / 3 博客。我有图表。 只是为了澄清:您的场景中的 EF4 是模型在我的图表中的位置,而您场景中的模型是我的图表中的专门模型管理器【参考方案5】:

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

【讨论】:

以上是关于存储库和服务层之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章

存储库和服务之间的区别?

使用 Spring 从控制器层调用存储库和服务

使用Spring从控制器层调用存储库和服务

使用 DTO 在服务层和 UI 层之间传输数据

我的 Entity Framework 存储库和服务层方法应返回哪些类型:List、IEnumerable、IQueryable?

EF6(代码优先)、MVC、Unity 和没有存储库的服务层