我将nHibernate用于ORM,将Ninject用于IoC。 我为每个自定义范围创建了nHibernate会话(您可以根据请求假设)。 我开始onActivation交易。 我将事务提交到Deactivation。
问题是如果在请求期间发生异常,我想回滚事务而不是提交它。任何想法如何检测(以一种干净的方式,最可能使用Ninject Context)发生异常?
注意:我并不关心在提交时可能发生的异常,我可以轻松地在下面的代码和角色中找到它。
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();
});
}
更新
我遵循了@BatteryBackupUnit的建议。 所以我将以下内容添加到Error EventHandler:
Error += (s, e) =>
{
HttpContext.Current.Items["ErrorRaised"] = true;
};
我将OnDeactivation修改为如下所示:
OnDeactivation(t =>
{
if ((bool?)HttpContext.Current.Items["ErrorRaised"] == true)
t.Session.Transaction.Rollback();
else
t.Session.Transaction.Commit();
t.Session.Dispose();
});
它工作正常,但如果Ninject通过在上下文中设置一个标志来解决这个问题会更好:如果发生异常:)
答案 0 :(得分:1)
如何实施IHTTPModule
并订阅Error
活动?
与描述here
在Error
事件处理程序中,使用System.Web.Mvc.DependencyResolver.Current.GetService(typeof (ISession))
检索当前会话并回滚事务。
但请注意,如果请求未使用会话,则会创建一个会话,这是非常多余的。
您可能会执行某些操作,例如检查事务是否已启动,然后再将其回滚。但是你还是会不必要地创建一个会话。
您可以使用Error
事件处理程序在HttpContext.Current.Items
上设置标记来进一步改进,例如
HttpContext.Current.Items["RollbackTransaction"] = true;
然后在会话的OnDeactivation
中使用它,如:
.OnDeactivation((c, t) =>
{
if(HttpContext.Current.Items.Contains("RollbackTransaction"])
{
t.Session.Transaction.Rollback();
}
else
{
t.Session.Transaction.Commit();
}
t.Session.Dispose();
});
请注意,HttpContext
是线程本地的,这意味着当您切换线程时,它可能是null
或者 - 最坏情况 - 它甚至可能是另一个HttpContext
。
请注意,我无法尝试,所以它可能无法正常工作。反馈意见。
答案 1 :(得分:0)
由于两个原因,我无法接受通过HttpContext传递状态。
经过大量的反复试验,我认为这应该是一个解决方案: 假设我们正在开发WebApi项目,所有操作的回滚事务一旦命中异常,就使用Ninject:
使用以下配置绑定ISessionFactory
和ISession
(我使用了此示例:Need a simple example of using nhibernate + unit of work + repository pattern + service layer + ninject),以及 ISessionInRequestScopeFactory
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.
接口ISessionInRequestScopeFactory
的代码:
public interface ISessionInRequestScopeFactory
{
ISession CreateSessionInRequestScope(); // return ISession in the request scope
}
对每个操作(https://github.com/ninject/Ninject.Web.WebApi/wiki/Dependency-injection-for-filters)使用 ninject过滤器注入 添加交易行为:
Kernel.BindHttpFilter<ApiTransactionFilter>(System.Web.Http.Filters.FilterScope.Action)
.WhenControllerHas<ApiTransactionAttribute>();
将[ApiTransaction]
属性添加到控制器:
[ApiTransaction]
public class YourApiController{ /* ... */}
所以我们现在将ApiTransactionFilter
绑定到YourApiController
[ApiTransaction]
属性
在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);
}
}