I have a problem with my insert with NHibernate
The transaction begin well, My select done correctly, My select next value from sequence too, And commit transaction, But no insert appear in my NHprofiler and no Errors appears. I use Session.OpenSession(ReadCommited) & Transaction Any idea of what happens ?
Code
class NHUnitOfWok : INHibernateUnitOfWork
{
private readonly ISession _session;
private bool _isDisposed;
private IsolationLevel _isolationLevel;
public NHUnitOfWok(ISession session)
{
_session = session;
_session.FlushMode = FlushMode.Never;
_isolationLevel = IsolationLevel.ReadCommitted;
}
internal ISession Session
{
get { return _session; }
}
public void SaveChanges()
{
Session.Flush();
}
public void CancelChanges()
{
Session.Clear();
}
public void Commit()
{
Session.Transaction.Commit();
}
public void Rollback()
{
Session.Transaction.Rollback();
}
public void WithinNewSession(Action<ISession> actionToExecute, IsolationLevel? isolationLevel = null)
{
using (var tempSession = Session.SessionFactory.OpenSession())
{
using (var transaction = tempSession.BeginTransaction(isolationLevel ?? _isolationLevel))
{
actionToExecute(tempSession);
transaction.Commit();
}
}
}
public void WithinTransaction(Action action, IsolationLevel? isolationLevel = null)
{
Enforce.NotNull(action, "action");
WithinTransaction<object>(() =>
{
action();
return null;
});
}
public T WithinTransaction<T>(Func<T> func, IsolationLevel? isolationLevel = null)
{
Enforce.NotNull(func, "func");
if (Session.Transaction != null && Session.Transaction.IsActive)
{
return func.Invoke();
}
using (var localTran = Session.BeginTransaction(isolationLevel ?? _isolationLevel))
{
try
{
var funcRes = func.Invoke();
localTran.Commit();
return funcRes;
}
catch (TransactionException ex)
{
throw new DataException(Resource.TransactionException, ex);
}
catch (Exception ex)
{
if (Session.Transaction.IsActive)
localTran.Rollback();
throw new DataException(Resource.TransactionException, ex);
}
}
}
public bool IsStarted()
{
return Session.Transaction != null && Session.Transaction.IsActive;
}
public void Start()
{
if (Session.Transaction == null || !Session.Transaction.IsActive)
{
Session.BeginTransaction(_isolationLevel);
}
}
private void Dispose(bool disposing)
{
if (!disposing || _isDisposed)
{
return;
}
_isDisposed = true;
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
FlushMode.Never
may be good for read-only transaction, but for anything else, well, probably not. Its xml documentation states:
The
ISession
is never flushed unlessFlush()
is explicitly called by the application. This mode is very efficient for read only transactions.
So do not use that by default.
That's my advice at least. I know there is some advices lurking around to use that for "performance" reasons, but check if you have still the issue by commenting that out.
Anyway, I never optimize for run-time performance first, developer performance should be the priority in my opinion. (Unless the application has actual, proven and corresponding run-time performance issues. Or, of course, when the code is an obvious coding horror in terms of run-time execution complexity, such as unmotivated O(n²) algorithms or worst.)
If you want to stick with that mode, call Flush
on session before committing transactions (or as written here by Andrew, choose FlushMode.Commit
). But really, this choice is a trap for developers having to work with it.
See the default mode documentation, Auto
:
The
ISession
is sometimes flushed before query execution in order to ensure that queries never return stale state. This is the default flush mode.
Are you willing to risk having subtle bugs due to getting unexpectedly stale data in queries occurring in the same session after some data changes?
Side note: why "complicating" code with Invoke
? See this.