【发布时间】:2020-02-02 03:01:55
【问题描述】:
我正在构建 3 层 ASP.NET Core Web API。它由数据、业务(核心)和 WebAPI 层组成:
- 核心层是独立的(不了解 EFCore 或任何其他项目)
- 数据层 - 引用 Core 和 EFCore
- WebAPI 层 - 最后一层,了解 Core、Data 和 EFCore
我正在努力决定如何处理数据库事务。我将 DbContext(作用域)注入到我的 Data 类和控制器(我省略了业务项目,因为它根本不知道 EFCore)。因为每个请求只有一个 DbContext 实例,所以它在 Data 和 Controller 中是同一个对象。
所以,业务逻辑正在做什么,应该做什么,调用数据层中的对象。每当数据层需要将更改保存到数据库中时,它都会这样做。一切都围绕着每个请求的事务。因此,如果出现问题...所有更改都会回滚。
这是显示我是如何做到的示例控制器的方法(简化):
[HttpPut("{id}")]
public IActionResult UpdateMeeting(int id, [FromBody] MeetingDto meeting)
{
using (var transaction = _dbContext.Database.BeginTransaction())
{
if (meeting == null)
{
return BadRequest();
}
_meetingService.AddMeetingChanges(meeting);
meeting.Id = id;
_meetingService.UpdateMeeting(meeting);
}
return NoContent();
}
一切都很好。那么问题是什么?我需要重复一遍:
using (var transaction = _dbContext.Database.BeginTransaction())
{
}
...在每个操作中,都需要事务。
所以我在想,是否可以在中间件/管道中启动事务(我不确定术语)。简单地说 - 我想根据每个请求明确地开始交易。我想隐藏在中间件中。这样每当我将 DbContext 注入 Data 类时,事务就已经开始了
编辑:可能的解决方案:
-
创建了一个
UnitOfWork类:public class UnitOfWork { private readonly RequestDelegate _next; public UnitOfWork(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext, MyContext ctx) { using (var transaction = ctx.Database.BeginTransaction()) { await _next(httpContext); transaction.Commit(); } } } -
在
UseHttpsRedirection和UseMvc之前注入UnitOfWork类作为中间件:public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(appBuilder => { appBuilder.Run(async context => { context.Response.StatusCode = 500; await context.Response.WriteAsync("An unexpected error happened. Please contact IT."); }); }); } app.UseHttpsRedirection(); app.UseMiddleware<UnitOfWork>(); app.UseMvc(); }
【问题讨论】:
-
使用中间件,注入上下文,在请求中包含一个可以由中间件重新检查的标志,以指示它应该在事务中包装管道中的下一个。确保中间件在管道中及早注册。
-
@Nkosi 谢谢。澄清一下,您的意思是:将 DbContext 注入实现
IAsyncActionFilter的类,然后将其添加到 Mvc 中?像这样:services.AddMvc(options => { options.Filters.AddService<CustomActionFilter>(); }? -
不是过滤器,是自定义中间件。
-
@Nkosi 哦,我明白了。我不确定让 API 的客户决定他是否要使用事务是否是个好主意。我想我会为每个请求创建事务。不好吗?我可以检查请求是否是“GET”,如果是,我不会开始交易。
-
这只是一个想法,并不难。使用 HTTP 动词是一个好主意和可行的选择。
标签: c# .net asp.net-core-mvc entity-framework-core asp.net-core-webapi