NHibernate issues with MVC application with session per request

I wrote C # MVC 3 with NHibernate as ORM and I have some odd exceptions imposed on most page loads. They seem to be mostly related to closed sessions and the like, and I've checked most of the general questions, but found little to help. Some of the exceptions include:

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
   at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.Data.SqlClient.SqlException (0x80131904): The server failed to resume the transaction. Desc:3b00000006.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.NullReferenceException: Object reference not set to an instance of an object.

    [[NHibernate.Transaction.AdoTransaction]] : Commit failed
    System.Data.SqlClient.SqlException (0x80131904): The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION.

    [[NHibernate.Util.ADOExceptionReporter]] : System.InvalidOperationException: The transaction is either not associated with the current connection or has been completed.
       at System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)

    [[NHibernate.Transaction.AdoTransaction]] : Begin transaction failed
    System.InvalidOperationException: SqlConnection does not support parallel transactions.

      

I apologize for the wall of exceptions, I suspect they are related to each other, but there could potentially be another error in the code causing one or two. I don't like to use the word "random" for these things, but I can't find any particular line of code that calls them, they just appear in lines of code that refer to ISession objects. I even had a "Session Closed" exception thrown by the BeginTranscation method in my Global.asax file.

The application uses the current_session_context_class web option in the hibernate.cfg.xml file.

My suspicion is that it has something to do with my session management code. The website usually loads about 10 concurrent AJAX requests and errors seem to happen more often when multiple pages are loaded at the same time. There are two session factories, one for each database in use.

Here is my corresponding Global.asax code:

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize();
            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
            _sessionFactory = (new WebClientSessionManager()).MakeSessionFactory();
            _sessionFactoryNotWeb = ClientSessionManager.MakeSessionFactory();
        }
protected void Application_BeginRequest(object sender, EventArgs e)
        {
            _session = _sessionFactory.OpenSession();
            _sessionNotWeb = _sessionFactoryNotWeb.OpenSession();
            CurrentSessionContext.Bind(_sessionNotWeb);
            CurrentSessionContext.Bind(_session);
            _session.BeginTransaction();
            _sessionNotWeb.BeginTransaction();
        }
 protected void Application_EndRequest(object sender, EventArgs e)
        {
            //Same code is repeated for the _sessionFactoryNotWeb
            ISession session = CurrentSessionContext.Unbind(_sessionFactory);
            if (session != null)
            {
                if (session.Transaction.IsActive)
                {
                    try
                    {
                        session.Transaction.Commit();
                    }
                    catch
                    {
                        session.Transaction.Rollback();
                    }
                }
                try
                {
                    session.Dispose();
                }
                catch
                {

                }
            }

      

I have looked at the page running in NHibernate profiler. Sometimes sessions don't start with BeginTranscation, sometimes they're not perfect, sometimes they're not; and most mysteriously, they sometimes run three times but are not finished.

Any calls to the ISession object are managed with this code (there is one for each factory):

public static ISession WebSession()
        {
            if (CurrentSessionContext.HasBind(MvcApplication._sessionFactory))
            {
                if (MvcApplication._sessionFactory.GetCurrentSession().IsOpen)
                {
                    return MvcApplication._sessionFactory.GetCurrentSession();
                }
                else
                {
                    log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Unbinding NHibernate session");
                    CurrentSessionContext.Unbind(MvcApplication._sessionFactory);
                    return WebSession();
                }
            }
            else
            {
                log4net.LogManager.GetLogger(typeof(DBHandler)).Debug("Initialising NHibernate session");
                var session = MvcApplication._sessionFactory.OpenSession();
                CurrentSessionContext.Bind(session);
                session.BeginTransaction();
                return session;
            }
        }

      

There are no calls to BeginTransaction or Commit in the whole application without clearing, committing, session and reopening them as per the code above. Any light you guys can shed on this would be greatly appreciated!

+3


source to share


1 answer


It seems that your session is stored in a global (widespread application) in your Global.asax.cs file. This means that the property contains the last session, not the session for a specific request. Two requests at the same time and you don't even know if you are still accessing the session you just created, because it may have already been overwritten by another session from the next request by running the same code. You don't have to store your session somewhere unrelated to the web request if you want to follow the Session-for-Request pattern. For example, you can store your NH session in HttpContext.Items-Collection. As another way to manage session in MVC, Ayende posted a good example of how to wrap session management around a single MVC action rather than the entire request.



+6


source







All Articles