【问题标题】:What way is better: make own wrapper for DbContext or use DbContext in Controller哪种方式更好:为 DbContext 制作自己的包装器或在 Controller 中使用 DbContext
【发布时间】:2016-01-06 09:00:39
【问题描述】:

在我的项目中,我使用 entity framework 7asp.net mvc 6 \ asp.net 5。我想为自己的模型创建 CRUD

我怎样才能做得更好:

  1. 使用控制器中的 dbcontext。 在下面link作者解释说这种方式比较好,但是否适合控制器?
  2. 制作自己的包装。 一些Best practices 写了关于什么是最好的自己的存储库。

我不会在其他地方更改 ef,所以请不要介意,即使有强大的连接性可以访问来自特定实现的数据 我知道在 ef7 dbcontext 中立即实现了工作单元和存储库模式。

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc entity-framework-core


    【解决方案1】:

    您的问题的答案主要是基于意见的。在回答许多其他问题之前,没有人可以明确地说“一种方式比另一种更好”。您的项目的规模/范围/预算是多少?有多少开发人员将致力于它?它会只有(基于视图的)MVC 控制器,还是也有(基于数据的)API 控制器?如果是后者,MVC 和 API 操作方法之间会有多少重叠(如果有的话)?它会有任何非 Web 客户端,如 WPF 吗?您打算如何测试应用程序?

    实体框架是一种数据访问层 (DAL) 工具。控制器是 HTTP 客户端请求和响应处理工具。除非您的应用程序是纯 CRUD(这是值得怀疑的),否则您可能需要在通过 HTTP 接收 Web 请求和使用 EF 将该请求的数据保存到数据库之间进行某种业务逻辑处理(字段 X 是必需的,如果您为字段 Y 提供数据,您还必须为字段 Z 提供数据等)。因此,如果您直接在控制器中使用 EF 代码,这意味着您的业务处理逻辑几乎肯定会与它一起出现在控制器中。

    我们这些在使用 .NET 开发重要应用程序方面拥有丰富经验的人倾向于形成这样的观点,即控制器中不应存在业务逻辑和数据访问逻辑,因为在实施此类设计时会出现某些困难。例如,当您将 web/http 请求和响应逻辑以及业务逻辑和数据访问逻辑放入控制器时,您最终不得不从控制器操作本身测试所有这些应用程序方面(这明显违反了 Single责任原则,如果你关心 SOLID 设计)。还假设您开发了一个带有返回视图的控制器的传统 MVC 应用程序,然后决定将该应用程序扩展到其他客户端,例如 iOS / android / WPF / 或其他不了解您的 MVC 视图的客户端。如果您决定实施第二组基于 WebAPI 数据的控制器操作,您将在至少 2 个地方复制业务和数据访问逻辑。

    不过,这并没有决定让控制器中的所有业务和数据访问逻辑本质上比替代设计“更糟糕”。您在设计 Web 应用程序架构时做出的任何决定都会有优点和缺点。无论您选择哪种路线,总会有取舍。将所有应用程序代码保留在控制器中的优势包括降低成本、降低复杂性,从而缩短上市时间。为非常简单的应用程序过度设计复杂的架构是没有意义的。然而不幸的是,我个人从未有过开发简单应用程序的乐趣,所以我“普遍认为”将业务和数据访问代码保留在控制器中“可能不是”一个好的长期设计决策。

    如果你真的对替代品感兴趣,我会推荐reading thesetwo articles。它们是关于如何实现控制器可以使用的命令和查询 (CQRS) 模式的很好的入门书。 EF 确实实现了开箱即用的存储库和工作单元模式,但这并不一定意味着您需要“包装”它以便将数据访问代码移出控制器。祝您为您的项目做出这些决定好运。

    public async Task<ActionResult> Index() {
        var user = await query.Execute(new UserById(1));
        return View(user);
    }
    

    【讨论】:

    • 感谢您的回答和您的宝贵时间。该项目简单而小。我在业务逻辑层分离逻辑。它最初只是 CRUD EF 的包装器,也许以后我会添加特定的功能。
    【解决方案2】:

    通常我更喜欢使用 Repository 模式和 UnitOfWork 模式 (http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application) - 我在 UnitOfWork 实例对象中实例化 DbContext 并将 DbContext 注入到存储库中。之后,我在控制器中实例化 UnitOfWork,而控制器对 DbContext 一无所知:

    public ActionResult Index()
    {
        var user = unitOfWork.UsersRepository.GetById(1); // unitOfWork is dependency injected using Unity or Ninject or some other framework
        return View(user);
    }
    

    【讨论】:

    • 感谢您的回答。但是我认为您通过控制器做了很多逻辑还是我错了?
    • 其实恰恰相反——我尽可能地减少控制器中的逻辑,防止它直接知道和处理 DbContext 初始化和查询数据库。拥有一个 unitOfWork 实例使您可以通过将所有存储库组合在一个地方来从一个地方处理所有数据库操作。您有责任为每种类型的实体(或实体组)创建存储库,并通过 unitOfWork 变量在控制器中调用它们的方法。存储库中的方法执行实际的数据库操作。
    • 只是指出 DbContext 默认实现 UnitOfWork 模式 -> “我们的” UnitOfWork 用于封装 DbContext 和存储库实例。
    • 现在,我真的理解你了。谢谢详细解答
    • hmmm...为什么需要一个工作单元才能GetById?您是否将任何内容保存到数据库以执行查询?
    【解决方案3】:

    这取决于您的应用程序的生命周期。

    如果它会被使用、扩展和更改很多年,那么我会说创建一个包装器是一个不错的选择。

    如果它是一个小型应用程序,并且如您所说,您不打算将EntityFramework更改为另一个ORM,那么省去创建包装器的工作,并在控制器中直接使用它。

    【讨论】:

    • 感谢您的回答。我同意你的看法。但我想了解为什么在 DbContext 的 ef7 中添加了 UoW / Repository 模式?让开发人员做更少的工作?
    【解决方案4】:

    对此没有明确的答案。这完全取决于你想要做什么。 如果您想要代码可维护性,我建议您使用包装器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多