在MVC中,存储过程的业务逻辑应该放在哪里?

3
我希望在这里获得一些经验和解释,因为不同的来源给出了不同的建议。我完全是MVC的新手。我知道这个问题以前已经被问过,但我目前没有使用EF或Linq。
我有一个SQL数据库,其中包含许多存储过程。以前在WebForms中使用时,有一个业务层,其中包含用于调用过程并将数据集返回给页面的辅助方法。重要的是,这些过程通常会查询约20个表;页面不仅仅反映数据库结构(正如我在大多数MVC教程中看到的):
SQL database <--> stored procedures <--> business layer <--> web forms

我希望在正确的基础上开始学习并采取最佳方法,但我知道可能没有一个“正确”的答案。因此,如果您发帖,请解释一下“为什么”?


  • 存储过程逻辑(SQLCommand/业务方法等)应该放在模型或控制器中?

有一篇帖子建议不要这样做, 但保留业务层。 另一位专家 建议

[模型/实体] 不应该有除了从数据库返回的内容之外的任何附加方法

  • 如果保留业务层,那么方法是从哪里调用的(例如模型或控制器)?

  • 如果上面的答案是“都不是”,那是否意味着模型部分将不会被使用?

那几乎感觉像是没有做好事情,然而在这个教程中似乎就是这样。

  • 我应该将实体框架插入到模型层中以调用业务层吗?

那感觉像是过度设计,增加了所有这些额外的逻辑。


1
跟所有的事情一样,这取决于你想要付出多少努力。对于一个只有一个页面的小应用程序来说,适当的层和分离关注点/单元测试可能会过度设计。我回答这个问题是假设你想以“正确”的方式去做事情,即使这只是一个小的示例项目。 - Liath
2个回答

2

你的控制器应该收集构建用户当前查看页面所需的信息,仅此而已。

控制器应该引用业务逻辑层中的类。

例如,下面是你的控制器。它只是将http请求进行转换并调用业务逻辑。

public class MyController : Controller
{
   private IMyBusinessLogic  _businessLogic;
   public MyController(IMyBusinessLogic businessLogic)
   {
      _businessLogic = businessLogic;
   }

   [HttpPost]
   public ActionResult UpdateAllRecords()
   {
      _businessLogic.UpdateAllRecords();
      return Json(new Success());
   }
}

以及你的业务逻辑类

public class MyBusinessLogic : IMyBusinessLogic  
{
  public void UpdateAllRecords()
  {
    // call SP here
    using(SqlConnection conn = new...
  }
}

这样做有许多优点:

  • 您的业务逻辑完全与用户界面(UI)分离,演示层中没有数据库代码。这意味着您的控制器可以专注于自己的工作,代码不会被污染。
  • 您可以测试您的控制器,并查看当业务逻辑成功、抛出异常等情况时会发生什么。

为了获得额外的奖励分数,您应该研究创建数据访问层。

public void DataAccess : IDataAccess
{
  public void RunStoredProcedure(string spName)
  {
  }
}

现在你可以测试你的BLL是否正确调用和处理了SP结果! 针对问题评论进行扩展: 理想情况下,你的模型应该没有任何逻辑。它只应该表示构建页面所需的数据。你正在加载的对象代表系统中的实体,模型代表显示在页面上的数据。这通常是更轻量级的,并且可能包含额外的信息(比如他们的地址),这些信息在主要实体上不存在但会在页面上显示。
例如:
public class Person
{
   public int  PersonID {get;set;}
   public string Firstname {get;set;}
   public string Lastname {get;set;}
   public Address Address {get;set;}
}

该模型仅包含您想要显示的信息:

public class PersonSummaryModel
{
   public int  PersonID {get;set;}
   public string FullName {get;set;}
}

您可以将模型传递给视图以显示它(在这种情况下可能是 FullName 的列表)。许多人使用映射器类在这两者之间进行转换,有些人则在控制器中执行此操作。

例如:

public class PersonMapper
{
  public PersonSummaryModel Map(Person person)
  {
     return new PersonSummaryModel
     {
       PersonID = person.PersonID,
       FullName = string.Concat(person.Firstname, " ", person.Lastname)
     };
  }
}

你可以使用一些自动化的解决方案,例如AutoMapper来执行此步骤。

谢谢@Liath。我需要返回一个类型为<MyObject>的对象给控制器。我已经把它放在了一个 Model 中,但是你说它实际上应该在被BL/Controller调用的DAL中。这实际上意味着整个应用程序中的Model层基本上没有使用(除了登录模型)。这听起来正常吗? - EvilDr
1
@EvilDr 但是,是的,从业务逻辑/数据访问中加载实体,并让您的控制器将它们调整以在页面上显示。这可能看起来像很多类,但当项目开始增长并且您想要测试事物时,它会使生活变得更加轻松。 - Liath
很有帮助,谢谢。昨天我按照教程创建了一个登录页面(http://bit.ly/VSa8gj)。根据您的观点,“您的模型不应该包含任何逻辑”,那么教程代码写得不好吗(用户模型包含大量逻辑)?这在某种程度上导致了我对结构的困惑... - EvilDr
1
@EvilDr 看起来很不错,但就我个人而言,我不确定模型中是否应该有 IsValid 方法。如果是我写的话,我会从控制器方法调用业务逻辑来进行这个判断,因为我不喜欢 UI 类访问数据库。不同的开发者有不同的方法! - Liath
1
@EvilDr 哈哈,没问题!我认为它比asp.net有一个更陡峭的学习曲线,但一旦你开始运行,它非常强大和干净。 - Liath
显示剩余2条评论

1

你的控制器应该只涉及到编排视图构造。创建一个名为“数据访问层”或其他名称的单独类库,并创建一个类来处理调用存储过程、从结果创建对象等操作。对于如何处理这个问题,有很多不同的意见,但也许最好的方法是

View
|
Controller
|
Business Logic
|
Data Access Layer
|--- SQL (Stored procs)
       -Tables
       -Views
       -etc.
|--- Alternate data sources
       -Web services
       -Text/XML files
       -and son on.

如果你想学习层次和最佳方法,MSDN在此链接上有很棒的文章MSDN


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接