How should NHibernate exceptions be handled during sessions ? There are many examples over the internet:
http://nhibernate.info/doc/nh/en/index.html#manipulatingdata-exceptions
https://svn.code.sf.net/p/nhibernate/code/trunk/nhibernate/src/NHibernate/ISession.cs
And many, many StackOwerflow threads that suggest an approach similar to this:
using (ISession session = factory.OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
try
{
// do some work
...
tx.Commit();
}
catch (Exception e)
{
if (tx != null) tx.Rollback();
throw;
}
}
But what if an error happens and an exception is thrown ON the 1st line of code(When you're opening a session) ? None of the examples cover it up!
A colege of mine sugested this approach:
ITransaction transaction = null;
try
{
using (ISession session = databaseFacade.OpenSession())
{
transaction = session.BeginTransaction();
//do some work
...
transaction.Commit();
}
}
catch (Exception ex)
{
if (transaction != null)
transaction.Rollback();
throw new Exception(ex.Message);
}
I suggest decoupling components that
open session
perform db operations
with this approach you can keep logic for handling OpenSession() exceptions inside your 1st line and don't worry later. Reason is that if (as in your case) databaseFacade.OpenSession() throws exception you don't have to catch it and check for transaction since it must be null
//if OpenSession() throws it's fine , not transaction at all
using (ISession session = databaseFacade.OpenSession())
{
using (ITransaction tx = session.BeginTransaction())
{
try
{
// do some work
...
tx.Commit();
}
catch (Exception e)
{
//tx is not null at this point
tx.Rollback();
throw;
}
}
}
Related
I have the flowing transaction code :
using (var context = new StDbContext())
{
context.Database.Log = Console.Write;
if (!context.Database.Connection.State.HasFlag(ConnectionState.Open))
{
context.Database.Connection.OpenAsync();
}
using (var transaction = context.Database.BeginTransaction())
{
try
{
var unit_of_work = new UnitOfWork(context);
unit_of_work.Sessions.AddModelSession(Model, unit_of_work);
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
TestBaseCore.mNLogLoggerService.Error(ex.Message);
}
}
}
Inside unit_of_work.Sessions.AddModelSession(); I'm saving data for different tables (UnitOfWork.Project.AddProject(); .... UnitOfWork.Fixture.AddFixture() ..and so on)->but sometimes the code is crashing in `unit_of_work.Sessions.AddModelSession();' and I'm not able to catch any exception using try catch?
Is there a way to log in a log file all the database operations to be able to figure it out whats the real issue there?
I'm trying to fix a bug in some legacy code. The code is trying to delete an object (a row) from the database. The Session, however, sometimes does not contain the object and throws an exception. I've added the test to see if the Session contains the object and then try various things to get the object into the Session, but no success thus far. Can you suggest what else I can try?
This code has implemented nHibernate Session very poorly in that it does not follow the Unit of Work ethic. The application creates one session when it starts and uses that session throughout the life of the application. Nothing I can do about this (other than a complete rewrite, in which case I would abandon nHibernate anyway).
using (ITransaction transaction = this.Session.BeginTransaction())
{
try
{
if (Session.Contains(object))
{
Session.Delete(object);
transaction.Commit();
}
else // new code, Session does not contain object
{
try
{
//Session.Close();
//Session.Dispose();
//Session.Disconnect();
Session.Merge(object);
Session.Save(object); // this does something
Session.Lock(object, LockMode.Upgrade); // .Write in invalid mode
Session.Update(object);
using (ITransaction transaction2 = this.Session.BeginTransaction())
{
// this code executes with no error but does not delete the row from the database
Session.Delete(object);
transaction2.Commit();
}
}
catch (System.Exception exception)
{
string message2 = exception.ToString();
MessageBox.Show(message2, "Delete() Try it again ", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
catch (System.Exception exception)
{
if (transaction != null)
{
transaction.Rollback();
}
throw exception;
}
}
Why don't you get the object from the session first?
using (ITransaction transaction = Session.BeginTransaction())
{
var persisted = Session.Get<SomeType>(object.Id); //object will be loaded if not already in the session
Session.Delete(persisted);
transaction.Commit();
}
public static void SetStatus( Status statusObject,int retryCount)
{
if (statusObject != null)
{
using (SqliteConnection dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
}
catch (Exception e)
{
dbTransaction.Rollback();
dbConn.Close();
if (retryCount > 0)
{
SetStatus(statusObject, --retryCount);
return;
}
else
throw e;
}
finally
{
try { dbTransaction.Commit(); }
catch (Exception e)
{
}
}
}
}
}
Whenever ExecuteNonQuery fails due due to some exception I have a retry mechanism which will run the same query again.In that case during the second time(while retrying) the following exception comes-
"Connection must be valid and open to commit transaction"
The problem is basically, that you commit the transaction in the finally block—which runs always per design—even though you may already have rolled back the transaction and closed the connection in the catch block.
The easier way would be to commit the transaction while still in the try block, because after all, you only want to commit the transaction if all the code in the try succeeded.
That being said, you are having some odd nesting with the try and using constructs there. You should try to keep things like usings as concise as possible. That way you will also not have to deal with closing connections yourself. Something like this:
public static void SetStatus(Status statusObject, int retryCount)
{
if (statusObject == null)
return;
try
{
using (var dbConn = new SqliteConnection(dbURL))
{
IDbTransaction dbTransaction = null;
try
{
dbConn.Open();
dbTransaction = dbConn.BeginTransaction();
new SqliteCommand(some_query, dbConn).ExecuteNonQuery();
dbTransaction.Commit();
}
catch
{
// transaction might be null if the connection couldn’t be opened
dbTransaction?.Rollback();
throw;
}
}
}
catch (Exception ex)
{
if (retryCount > 0)
SetStatus(statusObject, --retryCount);
else
throw ex;
}
}
One final note, one could argue that using a transaction is absolutely not necessary here: You are only executing a single query on this database connection, so either that works, or it will fail in which case there is not something to rollback anyway. So unless you are having multiple queries there, you don’t need an explicit transaction (but I’ll just assume that you just reduced your example to show just a single query).
My bad,screwed up using "finally" improperly myself.So if the query fails at first attempt it would go to catch block which will fire the call to re run the query.Second time query execution would be successful and transaction would be committed.Then the control goes to finally of first query which will again try to commit the already rolled back transaction. So,it would throw that exception.
So,if the moving the commit to main try block fixed the issue.
What is the right way to handle exception inside nested using statement? I have the following part of code:
public void Create(Entity entity)
{
try
{
using (ISession session = NhibernateHelper.OpenSession())
{
try
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(entity);
transaction.Commit();
}
}
catch (TransactionException transactionException)
{
// log it
throw;
}
}
}
catch (TransactionException transactionException)
{
// log it
throw;
}
catch (SessionException sessionException)
{
// log it
throw;
}
catch (Exception exception)
{
// log it
throw;
}
}
I saw few answers when people put try/catch statement inside nested using. I know that using statement consists of try/finally. And my question is about right way to catch all possible exceptions. Should I do rollback inside one of catch statements? Could you provide me the right way to do it?
I tend to minimize "try catch" clauses as much as possible.
Nesting "try catch" and logging each catch (while not swallowing exception) may cause a same exception to be logged multiple times, which bloats the logs. And having "try catch" everywhere bloats the code.
I do not see the need for explicitly rollbacking a failed transaction : non committed transaction are rollbacked on dispose by default.
My usual pattern with MVC/webform is to use a global action filter (usually derived from HandleErrorAttribute) for logging exception, and/or a dedicated IHttpModule. So no need for "try catches" anywhere else just for logging while not swallowing the exception.
(With MVC, I usually explicitly rollback failed transaction because I am using an action filter for opening them OnActionExecuting, committing or rollbacking them OnActionExecuted depending on filterContext state. There, especially if there was an error, I may add a swallowing and logged try catch around the rollback: it may fails too, and I consider this failure should not mask the one having caused the app to try rollbacking.)
I follow the pattern of always beginning a try..catch block immediately after BeginTransaction().
Other than that, I think it's fine to not add any other exception checking - just let it propagate out to the calling code.
using (var session = NhibernateHelper.OpenSession())
{
using(var transaction = session.BeginTransaction())
{
try
{
//
// Add a block of code here which queries and
// modifies data
//
//
transaction.Commit();
}
catch(Exception ex)
{
transaction.RollBack();
throw;
}
}
}
For saving a list of (virtual) bank account transactions, I want the business entity to reflect the state saved to database, also in case of an exception.
Can I assume that an exception here also means the transaction is rolled back? Or can I explicitly rollback in the catch to be sure? If so, what if that line throws an exception?
In Repository< T >:
public void SaveOrUpdate(IList<T> entityList)
{
using (ISession session = FluentNHibernateManager.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
try
{
foreach (T entity in entityList)
session.SaveOrUpdate(entity);
transaction.Commit();
}
catch (Exception e)
{
MyTrace.Exception(e.ToString());
// add this line? transaction.Rollback();
throw;
}
}
}
}
In Some Class:
cashTransactions.Add(t);
try {
GenericRepository<CashTransaction> repo = new GenericRepository<CashTransaction>();
repo.SaveOrUpdate(cashTransactions);
} catch (Exception ex) {
cashTransactions.Remove(t);
}
You can't assume that the transaction was rolled back, but you don't have to assume: ITransaction has a bool WasCommitted property.
You can check that to determine whether the transaction was committed, and call Rollback() explicitly, where warranted.
You've gotta include the rollback() call to rollback the transaction properly.
To dispose a not-commited transaction will always rollback it.
This is true in all ado.net transactions implementation, and of course at runtime NHibernate will use your chosen ado.net provider.