在请求使用Ninject管理会话期间发生异常时,如何回滚nHibernate事务?
我用nHibernate表示ORM,用Ninject表示IoC。 我根据一些自定义范围创建nHibernate会话(您可以假设它是根据请求创建的)。 我在激活时开始事务处理。 我在激活时提交事务 问题是,如果在请求期间发生异常,我希望回滚事务,而不是提交它。你知道如何检测(以一种干净的方式,最有可能使用Ninject上下文)发生了异常吗 注意:我不关心提交时可能发生的异常,我可以在下面的代码和角色中轻松捕获这些异常在请求使用Ninject管理会话期间发生异常时,如何回滚nHibernate事务?,nhibernate,transactions,ninject,rollback,Nhibernate,Transactions,Ninject,Rollback,我用nHibernate表示ORM,用Ninject表示IoC。 我根据一些自定义范围创建nHibernate会话(您可以假设它是根据请求创建的)。 我在激活时开始事务处理。 我在激活时提交事务 问题是,如果在请求期间发生异常,我希望回滚事务,而不是提交它。你知道如何检测(以一种干净的方式,最有可能使用Ninject上下文)发生了异常吗 注意:我不关心提交时可能发生的异常,我可以在下面的代码和角色中轻松捕获这些异常 protected void BindWithSessionWrapper<
protected void BindWithSessionWrapper<T>(Func<IContext, T> creationFunc) where T : ISessionWrapper
{
Bind<T>().ToMethod(creationFunc)
.InScope(x => new NinjectCustomScope()) // work in progress !!!
.OnActivation(t => t.Session.BeginTransaction(IsolationLevel.ReadCommitted))
.OnDeactivation((c, t) =>
{
t.Session.Transaction.Commit();
t.Session.Dispose();
});
}
我对激活进行了如下修改:
OnDeactivation(t =>
{
if ((bool?)HttpContext.Current.Items["ErrorRaised"] == true)
t.Session.Transaction.Rollback();
else
t.Session.Transaction.Commit();
t.Session.Dispose();
});
它工作得很好,但是如果Ninject在发生异常时通过在上下文中设置一个标志来解决这个问题,那就更好了:)如何实现
IHTTPModule
并订阅错误
事件?
如所述
在Error
事件处理程序中,使用System.Web.Mvc.DependencyResolver.Current.GetService(typeof(ISession))
检索当前会话并回滚事务
但是,请注意,如果请求没有使用会话,那么将创建一个会话,这是非常多余的
您可以执行一些操作,例如检查事务是否已启动,然后再回滚。但您仍然会创建一个不必要的会话
您可以通过使用错误
事件处理程序在HttpContext.Current.Items
上设置一个标志来进一步改进这一点,如
HttpContext.Current.Items["RollbackTransaction"] = true;
然后在会话的激活中使用它,如:
.OnDeactivation((c, t) =>
{
if(HttpContext.Current.Items.Contains("RollbackTransaction"])
{
t.Session.Transaction.Rollback();
}
else
{
t.Session.Transaction.Commit();
}
t.Session.Dispose();
});
请注意,HttpContext
是线程本地的,这意味着当您切换线程时,它可能是null
或者-最坏的情况-它甚至可能是另一个HttpContext
还请注意,我无法尝试它,因此它可能无法工作。感谢反馈。我不接受通过HttpContext传递状态,原因有二
HttpContext问题:)
传递状态就像传递全局状态()
经过多次尝试和错误,我认为这应该是一个解决方案:
假设我们正在处理WebApi项目,一旦遇到异常,所有操作都有回滚事务,使用Ninject:
安装Ninject.Extension.Factory(),这对于将请求范围中的ISession注入过滤器来说是非常重要的一步
使用以下配置绑定ISessionFactory
和ISession
(我使用了这个示例:),再加上ISessionInRequestScopeFactory
使用ninject filter injection将事务行为添加到每个操作():
因此,我们现在将ApiTransactionFilter
绑定到具有[ApiTransaction]
属性的YourApiController
在ApiTransactionFilter
内部,您应该扩展AbstractActionFilter
并注入工厂ISessionInRequestScopeFactory
,以获得正确的请求范围会话:
public class ApiTransactionFilter : AbstractActionFilter{
private readonly ISessionInRequestScopeFactory factory;
public ApiTransactionFilter(ISessionInRequestScopeFactory factory){
this.factory = factory;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory
session.BeginTransaction(); // session can begin transaction here ...
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory
if (actionExecutedContext.Exception == null) // NO EXCEPTION!
{
session.Transaction.Commit();// session commit here ... may be you like to have try catch here
}
else
{
session.Transaction.Rollback(); // session rollback here ...
}
base.OnActionExecuted(actionExecutedContext);
}
}
使用HttpContext是非常危险的:,HttpContext.Current在某些情况下可能会变为null。True,尤其是使用async/await
时,这可能会变得非常危险。
Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
Bind<ISession>()
.ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession())
.InRequestScope(); // notice that we don't need to call `BeginTransaction` at this moment
Bind<ISessionInRequestScopeFactory>().ToFactory(); // you don't need to make your implementation, the Ninject.Extension.Factory extension will help you so.
public interface ISessionInRequestScopeFactory
{
ISession CreateSessionInRequestScope(); // return ISession in the request scope
}
Kernel.BindHttpFilter<ApiTransactionFilter>(System.Web.Http.Filters.FilterScope.Action)
.WhenControllerHas<ApiTransactionAttribute>();
[ApiTransaction]
public class YourApiController{ /* ... */}
public class ApiTransactionFilter : AbstractActionFilter{
private readonly ISessionInRequestScopeFactory factory;
public ApiTransactionFilter(ISessionInRequestScopeFactory factory){
this.factory = factory;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory
session.BeginTransaction(); // session can begin transaction here ...
base.OnActionExecuting(actionContext);
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
ISession session = factory.CreateSessionInRequestScope(); // get the request scope session through factory
if (actionExecutedContext.Exception == null) // NO EXCEPTION!
{
session.Transaction.Commit();// session commit here ... may be you like to have try catch here
}
else
{
session.Transaction.Rollback(); // session rollback here ...
}
base.OnActionExecuted(actionExecutedContext);
}
}