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


188

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

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

Answers:


327

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

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

服务层->存储库层-> 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...}     
             }
         );
     }
}

2
@Sam Striano:如上所示,我的IRepository返回IQueryable。这允许在服务层中添加其他where条件并推迟执行,而不是稍后。是的,我使用一个程序集,但是所有这些类都放置在不同的名称空间中。没有理由在小型项目中创建许多程序集。命名空间和文件夹分离效果很好。
LukLed 2011年

81
为什么要在服务中返回视图模型?如果您要有多个客户端(移动/网络),该服务是否不应该模拟?如果真是这样,那么视图模型可能与不同的平台有所不同
Ryan

12
同意@Ryan,服务层应返回实体对象或实体对象的集合(不可IQueryable)。然后在ui上,例如,通过Automapper将实体映射到SomeViewModel。
Eldar

5
@Duffp:您不必为每个实体创建存储库。您可以使用通用实现并绑定IRepository<>GenericRepository<>IOC库中。这个答案很老。我认为最好的解决方案是将所有存储库合并为一个类UnitOfWork。它应该包含每种类型的存储库和一个称为的方法SaveChanges。所有存储库应共享一个EF上下文。
LukLed 2013年

2
而不是从服务层返回viewmodel,您应该返回DTO并使用automapper将其转换为viewModels。有时它们是相同的,否则,您将很感激地实现了YGTNI“需要”
hanzolo 2014年

41

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

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

[Controller]呼叫[Repository]的[Service]

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

在控制器中,这是因为它有助于使我保持干燥。我经常需要在其他地方使用相同的过滤或逻辑,如果将其放在控制器中,则无法重用。

在存储库中,这是因为我希望能够在出现更好的情况时更换存储(或ORM)。而且,如果我在存储库中有逻辑,则在更改存储库时需要重写此逻辑。如果我的存储库仅返回IQueryable,而该服务执行过滤,则只需要替换映射即可。

例如,我最近用EF4替换了我的Linq-To-Sql几个存储库,而那些忠于这一原则的存储库可能会在几分钟内被替换。在我有逻辑的地方,要花几个小时。


我同意你Mikael。实际上,我在技术博客freecodebase.com中应用了相同的场景,并且在此实现中使用了代码优先方法。源代码也可以在这里下载。
太妃糖

我一直在研究在现有MVC应用程序中应用存储库模式的一般主题。这是一个定制框架,具有类似于Active Record的ORM和其他Rails / Laravel约定,并且对于我现在正在做的工作存在一些体系结构问题。我遇到的一件事是,存储库“不应返回ViewModel,DTO或查询对象”,而应返回存储库对象。我正在考虑服务通过什么方法与存储库对象进行交互,onBeforeBuildBrowseQuery并且可以使用查询生成器来更改查询。
calligraphic-io

@Toffee,您的链接已损坏,可以更新吗,我需要此实现的源代码。
Hamza Khanzada

24

公认的答案(并被投票数百次)有一个重大缺陷。我想在评论中指出这一点,但它只会被埋在30条评论中,因此请在此处指出。

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

在您犯同样的错误之前,除了上述我的故事以外,还有其他一些原因:

从服务层返回ViewModel是一个很大的不。这就像在说:

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

  2. 这些服务假设它们将显示在某个地方的UI中。如果非UI应用程序(例如Web服务或Windows服务)使用它怎么办?

  3. 那甚至不是真正的ViewModel。真正的ViewModel具有可观察性,命令等。这只是POCO的名称不正确。(有关名称为何重要的信息,请参见上面的我的故事。)

  4. 消费应用程序最好是表示层(该层使用ViewModels),并且更好地理解C#。另一个哦!

拜托,不要那样做!


3
我只需要对此发表评论,尽管我知道它并没有增加讨论的内容:“那只是个名字不好的POCO。” <<-在T恤上看起来不错!:) :)
Mephisztoe

8

通常,存储库用作填充您的实体的支架-服务层会出去并发出请求。您可能会将存储库放在服务层下。


因此,在使用EF4的ASP.NET MVC应用程序中,也许像这样:SQL Server-> EF4->存储库->服务层->模型->控制器,反之亦然?
山姆

1
是的,您的存储库可用于从EF4获取轻量级实体。并且您的服务层可用于将其发送回专门的模型管理器(在您的方案中为模型)。控制器将要求您的专业模型经理来执行此操作...快速浏览我的Mvc 2/3博客。我有图表。
CarneyCode

为了澄清起见:您的方案中的EF4是模型在我的图表上的位置,而您的方案中的模型是我的图表中的专业模型管理器
CarneyCode 2011年

4

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.