控制器调用存储库而不是服务是不好的做法吗?


43

控制器调用存储库而不是服务是不好的做法吗?

解释更多:

我发现,在好的设计控制器中,可以调用服务和服务使用存储库。

但有时在控制器中,我不需要任何逻辑,只需要从db中获取并将其传递给视图即可。

而且我可以通过调用存储库来做到这一点-无需调用服务-这是不好的做法吗?


您如何致电服务?通过REST接口?
罗伯特·哈维

2
我自己通常使用这种设计方法。我的控制器(或基础的作曲家类)将向存储库请求数据或向其发送数据,然后将其传递给需要进行处理的任何服务类。没有理由将数据处理类与数据检索/管理类结合使用,尽管我知道典型的方法就是这样做,但它们是不同的关注点。
吉米·霍法

3
嗯 如果它是一个小型应用程序,而您只是想从数据库中获取数据,则服务层会浪费时间,除非该服务层是某些公共API(如REST接口)的一部分。“牛奶对您有好处还是对您有害?” 取决于您是否乳糖不耐症。
罗伯特·哈维

4
没有硬性规定要在Controller-> Repository上具有Controller-> Service-> Repository结构。为正确的应用选择正确的模式。我要说的是您应该使应用程序保持一致。
NikolaiDante

也许您可以提出一个通用服务,该服务仅将您的请求转发到存储库,然后返回。这对于保持统一的接口很有用,并且如果将来您需要添加真实服务以在调用存储库之前执行某些操作,则此操作很简单。
亨里克·巴塞洛斯

Answers:


32

不,可以这样想:存储库也是一种服务。

如果您通过存储库检索的实体可以处理大多数业务逻辑,则无需其他服务。仅拥有存储库就足够了。

即使您具有某些服务,也必须通过这些服务来操作实体。首先从存储库中获取实体,然后将其传递给所述服务。能够在尝试之前就抛出HTTP 404非常方便。

同样对于读取场景,通常只需要实体将其投影到DTO / ViewModel。介于两者之间的服务层通常会导致很多通过方法,这很丑陋。


2
说得好!我的首选是调用存储库,只有在这种情况下,存储库是不够的(即必须使用不同的存储库来修改两个实体),我创建了一个负责此操作的服务,并从控制器中调用它。
Zygimantas,

我注意到一个相当复杂的代码只是为了证明使用服务的合理性。荒谬,最少……
Gi1ber7

因此,我的存储库返回了我需要转换为“ xml对象”的“业务对象”列表,这是否足以拥有服务层?我在每个对象上调用一个方法,将其转换为另一种类型并添加到新列表中。
bot_bot

直接DAO访问在控制器中是危险的,它会使您容易受到SQL注入的攻击,并允许访问危险的操作,如,, deleteAll'',我绝对会避免使用它。
Anirudh

4

控制器直接调用存储库是不错的做法。“服务”只是另一种工具,因此请在有意义的地方使用它。

NikolaiDante评论:

...为正确的应用选择正确的模式。我要说的是您应该使应用程序保持一致。

我认为一致性不是最重要的方面。“服务”类旨在封装一些更高级别的逻辑,因此控制器不需要实现它。如果给定操作不需要“高级逻辑”,则直接进入存储库。

为了促进良好的关注点分离和可测试性,存储库应该是您通过构造函数注入到服务中的依赖项:

IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);

service.DoSomething(...);

如果在数据库中搜索记录需要某种形式的参数化查询,则服务类可能是接受视图模型并构建查询然后由存储库执行的好地方。

同样,如果您具有表单的复杂视图模型,则服务类可以通过在域模型/实体上调用方法,然后使用存储库来持久化记录,来封装创建,更新和删除记录的逻辑。

相反,如果您的控制器需要按其ID获取记录,则为此委派服务对象就象用大锤敲打图钉一样,远远超出了您的需求。

我发现控制器最适合处理事务或Unit of Work对象。然后,控制器或工作单元对象将委派给服务对象以进行复杂的操作,或者直接转到存储库以进行简单的操作(例如按ID查找记录)。

public class ShoppingCartsController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, ShoppingCartForm model)
    {
        // Controller initiates a database session and transaction
        using (IStoreContext store = new StoreContext())
        {
            // Controller goes directly to a repository to find a record by Id
            ShoppingCart cart = store.ShoppingCarts.Find(id);

            // Controller creates the service, and passes the repository and/or
            // the current transaction
            ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);

            if (cart == null)
                return HttpNotFound();

            if (ModelState.IsValid)
            {
                // Controller delegates to a service object to manipulate the
                // Domain Model (ShoppingCart)
                service.UpdateShoppingCart(model, cart);

                // Controller decides to commit changes
                store.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            else
            {
                return View(model);
            }
        }
    }
}

我认为将服务和直接与存储库混合使用是完全可以接受的。如果需要,您可以将事务进一步封装在一个工作单元对象中。

职责分解如下:

  • 控制器控制应用程序的流程
    • 如果购物车不在数据库中,则返回“ 404 Not Found”
    • 如果验证失败,则使用验证消息重新呈现表单
    • 如果一切都结帐,则保存购物车
  • 控制器委托一个服务类在您的域模型(或实体)上执行业务逻辑。服务对象不应实现业务逻辑!他们执行业务逻辑。
  • 控制器可以直接委派给存储库以进行简单操作
  • 服务对象在视图模型中获取数据,并委派给域模型以执行业务逻辑(例如,服务对象在调用存储库中的方法之前先调用域模型上的方法)
  • 服务对象委托给存储库以实现数据持久性
  • 控制器应:
    1. 管理交易的生命周期,或
    2. 创建工作单元对象以管理事务的生命周期

1
-1用于将DbContext放入控制器而不是仓库中。该回购旨在管理数据提供者,因此在数据提供者发生更改(例如,从MySQL更改为平面JSON文件,在一个位置更改)的情况下,
无需其他任何操作

@JimmyHoffa:我实际上是在回顾我编写的代码,说实话,我为我的存储库创建了一个“上下文”对象,而不是数据库。我认为DbContext在这种情况下是个坏名字。我会改变的。我使用NHibernate,并且存储库(或上下文,如果方便的话)管理数据库的工作,因此更改持久性机制不需要在上下文外部进行代码更改。
格雷格·伯格哈特

通过代码的外观,您似乎使控制器与存储库混淆了……我的意思是,您的“上下文”是完全错误的,并且绝对不应该存在于控制器中。
吉米·霍法

6
我不必回答,而且我不确定这是一个好问题,但是我投了反对票,因为我认为您的方法设计不好。不难理解,我只是劝阻控制器所拥有的上下文。IMO控制器不应该像这样启动和提交事务。这是其他许多地方的工作,我更喜欢控制器将所有不仅仅满足HTTP请求的请求委派给其他地方。
吉米·霍法

1
一个存储库,它通常负责所有数据上下文信息,以确保除了域本身需要知道的内容之外,其他任何信息都不需要了解有关数据的问题
Jimmy Hoffa,2016年

1

这取决于您的体系结构。我使用Spring,事务性始终由服务管理。

如果您直接调用存储库以进行写操作(或者简单地将没有委派给存储库的逻辑的简单服务)用于一项必须同时完成的操作的多个数据库事务。这将导致数据库中的数据不一致。通常,数据库操作应该工作或应该失败,但是半工作是令人头疼的原因。

因此,我认为直接从控制器中调用存储库或使用简单的委派服务是一种不好的做法。您开始只是为了阅读而做,不久之后,您或您的一个或您的伴侣就会开始进行写操作。

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.