I've got a problem with NHibernate 3.2.
I'm porting a solution we used in a Java application, to a c# 4.0 application. What we want to create is a simple mechanism that handle session and transaction through the NHibernate SessionFactory, letting the transaction be instantiated by the unit-of-work beginner, and then being used by all the partecipant method, without even know that they are part of a bigger unit-of-work. But if you call these sub methods directly, they'll handle their own transaction.
In these question I explained better our approach. We first did it in Java world, and it works pretty fine. Now I'm porting the the same approach to c# 4.0, using NHibernate 3.2.
The class which is going to handle all my session and transaction is colled OperationManager (you could think to UnitOfWorkManager):
public class OperationManager : IDisposable
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
ITransaction tx = null;
ISession session = null;
bool isInternalTransaction = false;
public ISession BeginOperation()
{
logger.Debug("Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
session = PersistenceManager.Istance.GetSession();
if (session.Transaction.IsActive)
{
isInternalTransaction = false;
tx = session.Transaction;
logger.Debug(GetCallerClassDotMethod() + " is binding to transaction " + tx.GetHashCode());
}
else
{
isInternalTransaction = true;
tx = session.Transaction;
tx.Begin();
logger.Debug("Transaction " + tx.GetHashCode() + " created by " + GetCallerClassDotMethod());
}
logger.Debug("Session hash " + session.GetHashCode());
return session;
}
private String GetCallerClassDotMethod() {
// needed to get the Business Logic method calling the public methods
var st = new StackTrace();
var sf = st.GetFrame(2);
var methodReference = sf.GetMethod().Name;
var classReference = sf.GetMethod().DeclaringType.FullName;
return string.Concat(classReference, ".", methodReference);
}
public void CommitOperation()
{
if (isInternalTransaction)
{
tx.Commit();
logger.Debug(GetCallerClassDotMethod() + " is committing transaction " + tx.GetHashCode());
}
}
public void RollbackOperation()
{
if (isInternalTransaction)
{
tx.Rollback();
logger.Debug(GetCallerClassDotMethod() + " is rolling back transaction " + tx.GetHashCode());
}
}
public void Dispose()
{
tx.Dispose();
session.Dispose();
}
}
Here's my PersistenceManager
internal class PersistenceManager : IDisposable
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static PersistenceManager _istance;
private ISessionFactory _SessionFactory;
private static Object _lock = new Object();
public static PersistenceManager Istance
{
get
{
lock (_lock)
{
if (_istance == null)
{
_istance = new PersistenceManager();
logger.Info("New PersistenceManager instance created");
}
return _istance;
}
}
}
private PersistenceManager()
{
// Initialize
Configuration cfg = new Configuration();
cfg.Configure(ConfigurationManager.ConfigurationManager.Istance.hibernateConfiguration);
cfg.SetProperty("connection.connection_string", ConfigurationManager.ConfigurationManager.Istance.connectionString);
/* Note: The AddAssembly() method requires that mappings be
* contained in hbm.xml files whose BuildAction properties
* are set to ‘Embedded Resource’. */
// Add class mappings to configuration object
System.Reflection.Assembly thisAssembly = typeof(PersistenceManager).Assembly;
cfg.AddAssembly(thisAssembly);
// Create session factory from configuration object
_SessionFactory = cfg.BuildSessionFactory();
}
public void Dispose()
{
_SessionFactory.Dispose();
}
/// <summary>
/// Close this Persistence Manager and release all resources (connection pools, etc). It is the responsibility of the application to ensure that there are no open Sessions before calling Close().
/// </summary>
public void Close()
{
_SessionFactory.Close();
}
public ISession GetSession()
{
return _SessionFactory.OpenSession();
}
}
Now, whenever I need to access DB, I use an OperationManager instance to grab the current session (and current transaction) and use it for al my need. An example is found here:
public IList<Agency> getAllAgencies()
{
using (var om = new OperationManager())
{
try
{
om.BeginOperation();
var result = base.Load<Agency>().ToList();
om.CommitOperation();
return result;
}
catch (Exception ex)
{
om.RollbackOperation();
throw ex;
}
}
}
and in the base class I have
protected IQueryable<T> Load<T>() where T : Model.ModelEntity
{
using (var om = new OperationManager())
{
try
{
var session = om.BeginOperation();
var entities = session.Query<T>();
om.CommitOperation();
return entities;
}
catch (Exception ex)
{
om.RollbackOperation();
throw new Exception(msg, ex);
}
}
}
The problem is that, even if I configured NHibernate session factory to work on a per-thread model using <property name="current_session_context_class">thread_static</property>
, the two calls to OperationManager.beginOperation() give back a difference sessione, thus with different transaction.
Can anybody tell me why this is happening?
Edited: Following the suggestion of Fredy Treboux, I tried to implement a mechanism that create a new session or just get the current one, using CurrentSessionContext static object of NHibernate. Too bad, this still don't work. After cleaning up the code, avoiding everything related on transaction, session, unit-of-work, etc, I wrote a very trivial class, and I figured out that using
<property name="current_session_context_class">thread_static</property>
bring me a problem with sql server 2008 db. Using that context class, on a classic SessionFactory.OpenSession() approach and then loading some data, I get the following error:
System.Data.SqlClient.SqlException: A transport-level error has occurred when receiving results from the server. (provider: Shared Memory Provider, error: 0 - Invalid Handle.)
Any idea why this happen?