Attach an object to a session - c#

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();
}

Related

How to log and intercept all database operations using Entity Framework?

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?

Connection must be valid and open to commit transaction for sqllite database while retrying to execute the failed query

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.

fluent NHibernate session exception handling

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;
}
}
}

Dispose not working, many dead connections

I'm getting strange things since updated to EF6,no sure this is related or not, but used to be good
I'm doing a set of work, then save it to DB , then do another , save another.
after a while,i check SQL server by sp_who2 , i found many dead connections from my computer.
Job is huge then there goes to 700 connections,
I have to kill them all manually in cycle.
program like:
while (jobDone == false)
{
var returnData=doOneSetJob();
myEntity dbconn= new myEntity;
foreach( var one in retrunData)
{
dbconn.targetTable.add(one );
try
{
dbconn.savechange();
/// even i put a dispose() here , still lots of dead connections
}
catch
{
console.writeline("DB Insertion Fail.");
dbconn.dispose();
dbconn= new myEntity();
}
}
dbconn.dispose()
}
You should consider refactoring your code so that your connection is cleaned up after your job is complete. For example:
using (var context = new DbContext())
{
while (!jobDone)
{
// Execute job and get data
var returnData = doOneSetJob();
// Process job results
foreach (var one in returnData)
{
try
{
context.TargetTable.Add(one);
context.SaveChanges();
}
catch (Exception ex)
{
// Log the error
}
}
}
}
The using statement will guarantee that your context is cleaned up properly, even if an error occurs while you are looping through the results.
In this case you should use a using statement. Taken from MSDN:
The using statement ensures that Dispose is called even if an exception occurs while you are calling methods on the object. You can achieve the same result by putting the object inside a try block and then calling Dispose in a finally block; in fact, this is how the using statement is translated by the compiler.
So, your code would look better like this:
using(var dbconn = new DbContext())
{
while (!jobDone)
{
foreach(var one in retrunData)
{
try
{
targetTable row = new TargetTable();
dbconn.TargetTable.add(row);
dbconn.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine("DB Insertion Fail.");
}
}
}
}
This way, even if your code fails at some point, the Context, resources and connections will be properly disposed.

Repository.SaveOrUpdate(), certainty of rollback executed on exception?

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.

Categories