【发布时间】:2016-02-09 22:40:29
【问题描述】:
我将 EF Core 1.0(以前称为 EF7)和 ASP.NET Core 1.0(以前称为 ASP.NET 5)用于 RESTful API。
我希望将某个工作单元限定为 http 请求,这样在响应 HTTP 请求时,对 DbContext 所做的所有更改都将保存到数据库中,或者不保存(例如,如果有一些例外)。
过去,我通过使用操作过滤器将 WebAPI2 与 NHibernate 一起用于此目的,在该过滤器中,我在执行操作时开始事务,并在执行操作时结束事务并关闭会话。这是http://isbn.directory/book/9781484201107推荐的方式
但是现在我正在使用 Asp.Net Core(虽然这不相关,但使用 Asp.Net Core Mvc)和 Entity Framework,据我所知,它已经实现了一个工作单元。
我认为将中间件插入 ASP.NET 管道(在 MVC 之前)将是正确的做事方式。所以一个请求会去:
PIPELINE ASP.NET:MyUnitOfWorkMiddleware ==> MVC 控制器 ==> 存储库 ==> MVC 控制器 ==> MyUnitOfWorkMiddleware
如果没有发生异常,我正在考虑让这个中间件保存 DbContext 更改,这样在我的存储库实现中我什至不需要执行 dbcontext.SaveChanges() 并且一切都会像集中式事务一样。在伪代码中,我猜它会是这样的:
class MyUnitOfWorkMiddleware
{
//..
1-get an instance of DbContext for this request.
try {
2-await the next item in the pipeline.
3-dbContext.SaveChanges();
}
catch (Exception e) {
2.1-rollback changes (simply by ignoring context)
2.2-return an http error response
}
}
这有意义吗?有没有人有类似的例子?我找不到任何好的做法或建议。
另外,如果我在 MVC 控制器级别使用这种方法,我将无法在 POST 新资源时访问由数据库创建的任何资源 ID,因为在保存 dbContext 更改之前不会生成 ID(稍后在在控制器完成执行后,在我的中间件的管道中)。如果我需要访问控制器中新创建的资源 ID 怎么办?
任何建议将不胜感激!
更新 1:我发现使用中间件实现此目的的方法存在问题,因为中间件中的 DbContext 实例与 MVC(和存储库)生命周期中的实例不同。见问题Entity Framework Core 1.0 DbContext not scoped to http request
更新 2:我还没有找到好的解决方案。到目前为止,基本上这些是我的选择:
- 尽快将更改保存在 DB 中。这意味着将其保存在存储库实现本身上。这种方法的问题在于,对于 Http 请求,我可能想使用多个存储库(即:将某些内容保存在数据库中,然后将 blob 上传到云存储)并且为了拥有一个工作单元,我必须实现一个处理多个实体甚至多个持久性方法(DB 和 Blob 存储)的存储库,这违背了整个目的
-
实现一个Action Filter,我将整个操作执行包装在一个数据库事务中。在控制器的动作执行结束时,如果没有异常,我将 chanches 提交给 DB,但如果有异常,我将回滚并丢弃上下文。这样做的问题是我的控制器的操作可能需要生成的实体 ID 才能将其返回给 http 客户端(即:如果我得到一个 POST /api/cars 我想返回一个 201 Accepted 并带有一个标识的位置标头在 /api/cars/123 创建的新资源和 ID 123 尚不可用,因为实体尚未保存在 DB 中且 ID 仍为临时 0)。控制器对 POST 动词请求的操作示例:
return CreatedAtRoute("GetCarById", new { carId= carSummaryCreated.Id }, carSummaryCreated); //carSummaryCreated.Id would be 0 until the changes are saved in DB
如何将整个控制器的操作包装在数据库事务中,同时让数据库生成的任何 Id 可用,以便在控制器的 Http 响应中返回它?或者.. 一旦提交了数据库更改,是否有任何优雅的方法可以覆盖 http 响应并在操作过滤器级别设置 Id?
更新 3: 根据nathanaldensr 的评论,我可以两全其美(将我的控制器的操作执行包装在数据库事务中_UoW 并且还知道新资源的 ID甚至在数据库提交更改之前创建)通过使用代码生成的 Guid 而不是依赖数据库来生成 Guid。
【问题讨论】:
标签: middleware unit-of-work entity-framework-core asp.net-core-1.0