When to make an NHibernate transaction?

Well familiar with Webforms and Linq, I am new to ASP.NET MVC and NHibernate World. I worked on a project using examples from Bob Cravens . My application mainly reads and does not write sequentially, so usually I will not use transactions. But to implement the "Unit of Work" template, all my research, including the Ayende blog , I have to say.

I have a problem -

  • 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 child objects of the object and save them in the consolidated 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 am going to add a “CommitNeeded” bool to UnitOfWork, which will be set by my Save () method and conditionally call Commit () on UnitOfWork.Dispose (). Is that a good idea?

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

Any tips to help fix my anti-pattern would be appreciated.


In response to the comments - I think I do not mind if they occur together or separately. Two things happen. The first modifies the Client object and then saves it. The second one records the record, 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 not modify the data. I believe that the Save call does not belong in any of these places. The question is where? Service Level Dispose () Method? or Filter, like other users?

+1
source share
2 answers

Using ASP.NET MVC, I use an action filter to bind a transaction scope to the controller runtime. This works most of the time, but you must be careful not to continue the transaction for 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 am using property nesting through a custom ControllerActionInvoker to get an IUnitOfWork dependency in an ActionFilterAttribute .

+5
source

I use the http module for this. I get the transaction at the beginning of the http request and end it at the end of the 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 is factory, it is simply initialized by Func when registering types in an 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 module

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

Source: https://habr.com/ru/post/899328/


All Articles