views:

77

answers:

2

Hi,

I am deleting things like this:

[Transaction]
        [AcceptVerbs(HttpVerbs.Post)]
        public RedirectToRouteResult DeleteQualitativeGlobalFeatureValue(string Id)
        {
            try
            {
                BlaService.DeleteBla(Id);
            }
            catch (Exception e)
            {
                ModelState.AddModelError("Exception", e.Message);
            }

            return RedirectToAction("Bladibla", new { Id = FeatureId });
        }

However, if something is ’illegally’ deleted (e.g. causing the violation of a referential constraint) I get a horrible exception which is not caught by my try catch block. I presume this has to do with the [Transaction] attribute. How can I avoid this to catch ANY exceptions in the controller method?

Thanks.

Best wishes,

Christian

+1  A: 

This is because actual commit and database-side validation happens on transaction commit.

You can use your own, slightly modifed version of the Sharp attribute.

public class TransactionAttribute: ActionFilterAttribute
{
  private TransactionAttributeHelper helper = new TransactionAttributeHelper();
  public override void OnActionExecuting(ActionExecutingContext filterContext)
  {
     helper.BeginTransaction();
  }

  public override void OnActionExecuted(ActionExecutedContext filterContext)
  {
     try
     {
        // notice that I rollback both on exception and model error, this helps a lot
        helper.FinishTransaction(filterContext.Exception == null &&
                                 filterContext.Controller.ViewData.ModelState.IsValid);
     }
     catch (Exception ex)
     {
        // here add ModelError, return error, or redirect
     }
  }
}

TransactionAttributeHelper is placed to .Data assembly to avoid NHibernate reference in .Controllers.

public class TransactionAttributeHelper
{
  public void BeginTransaction()
  {
     NHibernateSession.CurrentFor(GetEffectiveFactoryKey()).BeginTransaction();
  }

  public void FinishTransaction(bool commit)
  {
     string effectiveFactoryKey = GetEffectiveFactoryKey();

     ITransaction currentTransaction =
         NHibernateSession.CurrentFor(effectiveFactoryKey).Transaction;

     if (currentTransaction.IsActive)
     {
        if (commit)
        {
           currentTransaction.Commit();
        }
        else
        {
           currentTransaction.Rollback();
        }
     }
  }

  private static string GetEffectiveFactoryKey()
  {
     return NHibernateSession.DefaultFactoryKey;
  }
}

Alternatively, of course, you can do transations without the attribute using repository.DbContext.BeginTransaction/Commit/etc methods and catch/process errors manually. But the above approach saves from a lot of such manual work.

queen3
A: 

You should look into an attribute that implements the IExceptionFilter interface. For example the System.Web.Mvc.HandleErrorAttribute can display an alternate view for an exception and gives that view access to the exception. You can also create your own attributes that implement IExceptionFilter if you want to handle things differently or log the exception using log4net or Elmah.

Having an IExceptionFilter attribute on the method will catch the exception even if the exception occurs in the TransactionAttribute's code.

Dan