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.