我正在启动一个新的Web API应用程序,但我不确定如何处理事务(以及在异常情况下的后续回滚(。
我的总体目标是为每个请求提供一个数据库连接,并将整个事情包装在一个显式事务中。我将需要一个显式事务,因为我也将执行存储过程,并且如果我的应用程序引发任何异常,则需要回滚这些过程的任何结果。
我的计划是重用我过去在 MVC 应用程序中使用的一种方法,粗略地说,它只是使用 ninject 将我的数据库上下文绑定到 requestscope,然后在 ondeactivation 事件中处理回滚/提交。
假设我有一个具有两种方法的控制器。
public class MyController : ApiController {
public MyController(IRepo repo) {
}
}
public string SimpleAddElement() {
_repo.Add(new MyModel());
}
public string ThisCouldBlowUp() {
// read from context
var foo = _repo.ReadFromDB();
// execute stored prodecure which changes some content
var res = _repo.StoredProcOperation();
// throw an exception due to bug/failsafe condition
if (res == 42)
throw Exception("Argh, an error occured");
}
}
我的回购骨架
public class Repo : IRepo {
public Repo(IMyDbContext context) {
}
}
从这里开始,我的计划是简单地使用
kernel.Bind<IRepo>().To<Repo>();
并为每个请求提供单个数据库上下文
kernel.bind<IMyDbContext>().To<CreateCtx>()
.InRequestScope()
.OnDeactivate(FinalizeTransaction);
private IMyDbContext CreateCtx(IMyDbContext ctx) {
var ctx = new DbContext();
ctx.Database.BeginTransaction();
}
private void FinalizeTransaction(IMyDbContext ctx) {
if (true /* no errors logged on current HttpRequest.AllErrors */)
ctx.Commit();
else
ctx.Rollback();
}
现在,如果我从浏览器中调用 SimpleAddElement,FinalizeTransaction 永远不会被调用......所以要么我突然做错了什么,要么错过了与 WebAPI 管道相关的内容
那么我应该如何实现事务性"每个请求单个数据库会话"模块呢?什么是最佳实践?如果可能的话,我希望解决方案也支持 ASP vNext
我想一个潜在的解决方案可以放弃"ondeactivation"处理程序并实现一个 HTTP 模块,该模块将在 Endrequest 中提交并在错误中回滚......但
代码中缺少抽象。您在控制器中执行业务逻辑,这是错误的位置。如果将此逻辑提取到业务层并将其隐藏在抽象后面,则将所有业务层操作包装在事务中将变得微不足道。看看这篇文章,了解这方面的一些例子。