Entity Framework 和 ASP.NET MVC - 如何直接使用 DBContext 和 DBSet 作为 Repository 和 Unity of Work 模式?

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 6 作为 ORM 选择, 和 ASP.NET MVC 5.

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

让我详细说明作为定义体系结构起点得出的初步结论:

我想遵循有关层、抽象和所有解决方案组件职责的原则,以实现尽可能低的复杂性场景。作为练习,考虑到这个 "simplicity",我可以像创建新的 Visual Studio ASP.NET MVC Web 应用程序一样接受 Microsoft 的模板 "proposed",但是我相信这不符合企业应用程序所需的最低设计场景,因为在 Microsoft 的模板中,控制器直接使用 Entity Framework DbContext 并使用数据访问层,此外没有服务层。这会导致几个问题,例如极度紧密的耦合 在表示层和数据访问层之间,以及所谓的 "fat controller" 问题,在该问题中,控制器成为软件的臃肿部分,并承担了业务逻辑、事务等的所有附加职责,使其真正变得一团糟软件可维护性,例如,违反 DRY(不要重复自己)的最基本原则,因为您会在整个胖控制器上获得重复的代码和逻辑。

在从简单到复杂的道路上进入下一阶段,我认为在设计中添加一个服务层是公平的,因为这样 ASP.NET MVC 控制器将只与该服务层对话,谁将负责所有 CRUD 和 CRUD 操作的验证,以及所有其他更复杂的业务逻辑操作。然后,该服务层将与由 Entity Framework.

表示的数据访问层对话

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

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

我认为这个问题的答案只是我认为获得 "least complex viable design" 所必需的最后一次复杂性飞跃。

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

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

EntityOneService.Insert
EntityTwoService.Insert

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

OperationOnePlusTwoService.Insert

这个新方法将以类似交易的方式实现对 EntityOneService.Insert 和 EntityTwoService.Insert 的调用。

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

DBContext.SaveChanges() 显然只需要调用一次就可以实现,在任何服务层 Insert 方法实现之外。在里面 ASP.NET 控制器使用服务层方法的上下文,如果没有在 DBContext 和 DBSet 上实际实现工作单元和 Repostory 抽象,如何实现?

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

desired scenario is that I can use each service layer method call in an isolated way ... but that behave IN A TRANSACTION-LIKE FASHION.

这对于 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();
}

大卫