使用服务和存储库模式时如何以及在何处实现事务?

How and where to implement transactions when using the service and repository patterns?

我正在尝试在我的 ASP.NET 基于 Core 6 的 Web 应用程序(使用 EF Core)中实现服务和存储库模式,但我认为我做错了几件事。在我的控制器中,我注入了一个服务实例,然后我可以像这样使用它来获取、创建、更新或删除实体:

[HttpPost]
public async Task<IActionResult> CreateProject([FromBody] Project body)
{
    int projectId = await this.projectService.CreateProjectAsync(body);

    return CreatedAtAction(nameof(GetProject), new { id = projectId }, null);
}

CreateProjectAsync 函数然后执行验证(如有必要)并调用 ProjectRepository class 的相应 CreateProjectAsync 函数。需要注意的一件重要事情是 Project class 是我自己创建的,也用作视图模型。它在数据库中created/updated之前或从数据库中读取后映射到存储库中相应的EF Core类型(例如TblProject)。

这种方法在很多情况下都很好用,但我经常在需要使用事务时遇到问题。一个例子是,除了项目之外,我还想在创建新项目时同时创建相关实体。我只希望在项目和相关实体都成功创建时此操作成功,如果不使用事务我就无法做到这一点。但是,我的服务无法创建事务,因为它们不知道 EF Core DbContext class,所以我现在唯一的选择是直接在存储库中创建事务。这样做会迫使我在同一个存储库中创建所有内容,但到目前为止我看到的每个示例都建议不要在单个存储库中混合不同的实体。

在类似的项目中,这通常是如何完成的?我的架构有什么问题我应该考虑更改以使我的项目更轻松吗?

好问题!默认情况下,EF 上下文被注册为作用域依赖项 - 因此每个请求一个。您可以创建一个简单的事务服务(注册为作用域依赖项)来公开事务处理。该服务可以根据需要注入您的存储库和其他服务层。

通过在事务开始时返回事务对象,您可以授予每一层自主权来提交或回滚它自己的事务。

您还可以向事务服务的 Dispose 方法添加一些清理逻辑,以在处理服务时提交或回滚任何打开的事务。

个人练习编辑: 除了频繁的get操作外,我已经放弃了几个项目的存储库模式。直接在服务层中使用 EF 上下文使我能够利用导航属性来执行复杂的 multi-table 插入或编辑。我还获得了额外的好处,即单次往返数据库以执行这些操作,以及隐式事务包装。就个人而言,在 EF 项目上使用存储库模式变得越来越困难 - 除了你 真正 锁定到 EF 之外,因为它遍布较低的服务层。