【问题标题】:Entity Framework and ASP.NET MVC - how to directly make use of DBContext and DBSet to behave as Repository and Unity of Work patterns?Entity Framework 和 ASP.NET MVC - 如何直接使用 DBContext 和 DBSet 来充当 Repository 和 Unity of Work 模式?
【发布时间】:2026-02-16 16:40:01
【问题描述】:

我正在就解决方​​案的开发模式做出项目决策,其中涉及使用 Entity Framework 6 作为 ORM 选择, 和 ASP.NET MVC 5。

我需要深入了解如何实现事务和业务逻辑。关于层,我对设计进行了初步假设,其中 SQL Server 之上的实体框架可被视为数据访问层 (DAL)。在实体框架之上,将有一个服务层,将在其中实现业务逻辑和验证。在服务层之上,我将让 ASP.NET MVC 控制器使用服务层提供的内容。

让我详细说明这个初步结论,作为定义架构的起点:

关于层、抽象和所有解决方案组件的职责,我想遵循原则来实现可能的最低复杂度场景。作为练习,考虑到这种“简单性”,我可以接受微软“建议”的模板,就像你刚刚创建一个新的 Visual Studio ASP.NET MVC Web 应用程序一样,但我认为这不符合最低设计场景企业应用程序需要,因为在微软的模板中,控制器直接使用实体框架 DbContext 并使用数据访问层,除了不存在服务层的事实。这会导致几个问题,例如非常紧密的耦合 在表示层和数据访问层之间,以及所谓的“胖控制器”问题,控制器成为软件的臃肿部分,增加了业务逻辑、事务等的所有职责,使其真正变得一团糟软件可维护性,例如,违反了 DRY(不要重复自己)的最基本原则,因为您会在胖控制器上得到重复的代码和逻辑。

在从简单到复杂的道路上加速下一个阶段,我认为在设计中添加一个服务层是公平的,因为这样 ASP.NET MVC 控制器将只与这个负责的服务层对话用于所有 CRUD 和 CRUD 操作的验证,以及所有其他更复杂的业务逻辑操作。然后,该服务层将与实体框架所代表的数据访问层对话。

我会停在那里,并说具有这些建议层的设计就足够了,但这就是我需要更多了解如何进行的地方。我需要解决有关如何实现事务的问题,如果您将它们视为由负责验证的方法执行的一系列单独操作的包装,以及驻留在服务层内的类中的业务逻辑。在使用实体框架的实现方面,如果我让服务层方法执行的每个单独操作都发出 .SaveChanges(),我将失去让 DBContext 像工作单元一样工作的能力,包装单个 .SaveChanges () 用于许多单独的 DBSet 操作。在这种情况下,DBSet 的行为可能类似于存储库。许多人认为实体框架的 DBContext 和 DBSet 分别是工作单元和存储库模式的实现。

那么,在一个更客观的问题中,我如何直接使用 DBContext 和 DBSet 来实现这些模式,而无需进一步抽象为新的通用或特定实体存储库类和工作单元通用类?由于我已经说明的原因,实现需要依赖服务层的消耗。

我认为这个问题的答案只是我认为获得“最简单的可行设计”所必需的最后一次复杂性飞跃。

让我举一个更具体的例子来说明:

在我的服务层中,我有 2 种方法来实现 2 次插入操作的验证逻辑,使用程序员定义的方法 Insert,例如:

EntityOneService.Insert
EntityTwoService.Insert

它们对应的服务层类中的每个方法都可以访问 DBContext 并使用 DBSet.Add 来表示它们应该被持久化, 以防所有验证和/或业务逻辑都通过。期望的场景是我可以以隔离的方式和/或分组使用每个服务层方法调用,例如在新的不同服务层类方法中,例如:

OperationOnePlusTwoService.Insert

这个新方法将在 TRANSACTION-LIKE FASHION 中实现对 EntityOneService.Insert 和 EntityTwoService.Insert 的调用。

我所说的类事务是指所有调用都必须成功,不违反任何验证或业务规则,才能让持久层提交操作。

DBContext.SaveChanges() 显然只需要调用一次才能发生这种情况,在任何服务层插入方法实现之外。在里面 使用服务层方法的 ASP.NET 控制器的上下文,如果没有在 DBContext 和 DBSet 上实际实现工作单元和存储库抽象,如何实现?

任何建议都将不胜感激。我发布这篇文章并不是为了争论存储库和工作单元模式的真正抽象和实现的价值,或者实体框架的 DBContext 和 DBSet 是否等同于正确的存储库和工作单元模式,这不是重点。我的项目要求不涉及以任何方式将应用程序与实体框架分离,或者理想地和充分地促进可测试性。 这些都不是问题,我很清楚如果不采用完整的六层实现和所有可能的设计模式来制作大型世界级企业解决方案的后果和未来可维护性的影响。

【问题讨论】:

标签: asp.net-mvc entity-framework repository-pattern unit-of-work


【解决方案1】:

理想的情况是我可以以隔离的方式使用每个服务层方法调用......但它的行为类似于交易方式。

这对于 EF 来说相当简单,假设您的服务不是远程的(在这种情况下,首先不建议使用事务)。

在最简单的实现中,每个服务实例都需要在其构造函数中传递一个 DbContext,并将其更改贡献给该 DbContext。编排控制器代码可以控制 DbContext 的生命周期并控制其对事务的使用。

在实践中,通常使用接口而不是具体的服务类型,并且通常使用依赖注入而不是构造函数。但是模式是一样的。

例如

using (var db = new MyDbContext())
using (var s1 = new SomeService(db))
using (var s2 = new SomeOtherService(db))
using (var tran = db.Database.BeginTransaction())
{
  s1.DoStuff();
  s2.DoStuff();
  tran.Commit();
}

大卫

【讨论】:

  • 您好,非常感谢您的洞察力。当您作为示例提供的这个 sn-p 代码在 ASP.NET MVC 控制器操作方法中时,它是否确保每个 Web 请求使用一个 DBContext 实例?使用块链接的代码是否属于这种情况?每个 Web 请求一个 DBContext 真的是最佳实践吗?我在别处看到资料说DBContext的实例化和处置应该在Application_BeginRequest()和Application_EndRequest()中完成,有必要吗?
  • 我发现了一篇非常好的博客文章,它阐明了我所问的大部分内容。无论如何,请随时添加 cmets。 Managing Entity Framework DbContext Lifetime in ASP.NET MVC
最近更新 更多