When to commit NHibernate Transaction?

这一生的挚爱 提交于 2020-01-05 12:16:13

问题


While very familiar to Webforms and Linq, I am a novice to the ASP.NET MVC and NHibernate World. I have been working through a project using Bob Cravens' examples. My application is mostly reads and non-sequential writes, so typically I would not use a transactions. But to implement the Unit-of-Work pattern, all my research including Ayende's blog says I should.

The problem I have is this -

  • Ninject creates a Session and Opens a Transaction.
  • Ninject injects repositories into services, and services into controllers.
  • I make some changes to the properties and children of an object and save on the aggregate root. This calls Transaction.Commit (works fine, as expected)
  • In another method later in the controller, I try to save a separate object
  • The second call fails because the transaction is no longer active.

I'm thinking of adding a bool "CommitNeeded" to the UnitOfWork which would be set by my Save() method and conditionally trigger a Commit() on UnitOfWork.Dispose(). Is this a good idea?

Should I remove the transaction infrastructure? Should I change my Commit()'s to Flush()'s?

Any advice that would help fix my anti-pattern would be appreciated.


In response to the comments - I guess I don't mind if they happen together or separate. There are two things going on. The first one changes a "Customer" object, and then saves it. The second makes a logging entry which then calls the same "Save" method.

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

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

where LogEvent looks like

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

    Save(customer);
}

The RecordEvent method has its own "save" because it is called from other controllers that do no data changes. I believe the Save call doesn't belong in either of those places. The question is, where? The Dispose() method of the Service Layer? or a Filter like the other users suggested?


回答1:


Using ASP.NET MVC, I use an action filter to bind the transaction scope to the controller action execution lifecycle. This works great most of the time, but you have to be cautious not to keep transactions open too long.

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);
    }
}

In my case I'm using property injection via a custom ControllerActionInvoker to get the IUnitOfWork dependency into the ActionFilterAttribute.




回答2:


I'm using for that an http module. I get the transaction at the beginning of http request and terminate it on the end of http request :

 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();
            }
        }
    }

My unit of work factory it's just a Func initialized while registering types in IoC container :

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

Initialization (for my case StructureMap) :

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

And then you register you in UnitOfWorkModule web.config

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


来源:https://stackoverflow.com/questions/6535984/when-to-commit-nhibernate-transaction

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!