什么时候提交NHibernate Transaction?

时间:2011-06-30 14:02:11

标签: asp.net-mvc nhibernate transactions dependency-injection

虽然对Webforms和Linq非常熟悉,但我是ASP.NET MVC和NHibernate World的新手。我一直在使用Bob Cravens'examples完成一个项目。我的应用程序主要是读取和非顺序写入,因此通常我不会使用事务。但是为了实现工作单元模式,包括Ayende's blog在内的所有研究都说我应该。

我遇到的问题是这个 -

  • Ninject创建会话并打开交易。
  • Ninject将存储库注入服务,将服务注入控制器。
  • 我对对象的属性和子进行了一些更改,并保存在聚合根目录上。这会调用Transaction.Commit(正常工作,如预期)
  • 稍后在控制器中的另一种方法中,我尝试保存单独的对象
  • 第二次调用失败,因为事务不再处于活动状态。

我正在考虑向UnitOfWork添加一个bool“CommitNeeded”,它将由我的Save()方法设置并有条件地触发UnitOfWork.Dispose()上的Commit()。这是一个好主意吗?

我应该删除交易基础设施吗?我应该将Commit()改为Flush()吗?

任何有助于修复我的反模式的建议都将受到赞赏。


回应评论 - 我想我不介意它们是一起发生还是分开发生。有两件事情在发生。第一个更改“客户”对象,然后保存它。第二个创建一个日志记录条目,然后调用相同的“保存”方法。

var Customer = ServiceLayer.GetCustomer(...);
Transmogrify(Customer, Customer.Children, Widgets, ...);
ServiceLayer.Save(Customer)

ServiceLayer.RecordEvent(Customer, "Customer was frobbed")

其中LogEvent看起来像

public void RecordEvent(Customer customer, int eventTypeId, string description)
{
    ...

    Save(customer);
}

RecordEvent方法有自己的“保存”,因为它是从没有数据更改的其他控制器调用的。我相信保存电话不属于这两个地方。问题是,在哪里?服务层的Dispose()方法?或像其他用户建议的过滤器?

2 个答案:

答案 0 :(得分:5)

使用ASP.NET MVC,我使用动作过滤器将事务范围绑定到控制器操作执行生命周期。这在大多数情况下都很有用,但是你必须小心谨慎,不要让交易开放太长时间。

public class UnitOfWorkActionFilter : ActionFilterAttribute
{
    public IUnitOfWork UnitOfWork { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        UnitOfWork.Start();
        base.OnActionExecuting(filterContext);
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Exception == null)
        {
            UnitOfWork.Commit();
        }
        else
        {
            UnitOfWork.Rollback();
        }
        UnitOfWork.Dispose();
        base.OnActionExecuted(filterContext);
    }
}

在我的情况下,我通过自定义ControllerActionInvoker使用属性注入来获取IUnitOfWork的{​​{1}}依赖项。

答案 1 :(得分:1)

我正在使用这个http模块。我在http请求开始时获取事务,并在http请求结束时终止它:

 public class UnitOfWorkModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += context_BeginRequest;
            context.EndRequest += context_EndRequest;
        }

        private void context_BeginRequest(object sender, EventArgs e)
        {
            IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
            instance.Begin();
        }

        private void context_EndRequest(object sender, EventArgs e)
        {
            IUnitOfWork instance = UnitOfWorkFactory.GetDefault();
            try
            {
                instance.Commit();
            }
            catch
            {
                instance.RollBack();
            }
            finally
            {
                instance.Dispose();
            }
        }
    }

我的工作单元工厂只是在IoC容器中注册类型时初始化的Func:

public class UnitOfWorkFactory  
{
        public static Func<IUnitOfWork> GetDefault;
    }

初始化(对于我的案例StructureMap):

UnitOfWorkFactory.GetDefault = () => container.GetInstance<IUnitOfWork>();

然后在UnitOfWorkModule web.config

中注册
<httpModules>
      <add name="UnitOfWorkModule" type="UI.UnitOfWorkModule, UI" />
    </httpModules>