Re: ASP.Net WebForms Session Management advice?

144 views
Skip to first unread message

Jason Meckley

unread,
Jun 22, 2012, 12:31:02 PM6/22/12
to nhu...@googlegroups.com
session per request is the way to go. instead of holding the session in an Ioc place it in the request's context.

Transactions are a bit trickier with webforms. When using a client (code) side POID like hilo or guid NH doesn't actually issue any sql queries to the DB until the transaction is committed. this is in alignment with the concepts of a UoW. If a db generated id like identity is used, then NH must hit the DB as soon as you call Save or Delete to keep the object graph in sync with the database.

With MVC reads/GETS and writes/POSTs can be separated into unique requests using the Post/Redirect/Get workflow. This doesn't exist in Webforms because both commands and queries are issued in the postback.

So for transactions in webforms I like to create a simple Transaction wrapper extension method to encapsulate the begin/try-commit/catch-rollback/finally - dispose code.

On Friday, June 22, 2012 11:31:33 AM UTC-4, Paolo wrote:
Hi all,

I've been trying to come up with/find a 'best practice' route for NHib session management in ASP.Net WebForms.  I'm unable to use IoC containers etc due to architectural features of the code (it's a fixed framework we use internally).  What I'm looking for is some pointers on the solution I've come up with, as I'm sure it could be improved.

Keeping it simple, I'm trying to achieve session-per-request with no requirement for transactions.  The code below has an overall 'wrapping' transaction because I found if I did a delete then immediately re-loaded the list objects to the page in the same cycle, the deleted object would 're-appear', even though I'd just deleted it (object disappears if I do a page refresh).  Not entirely sure why this is if I'm honest!  Do I need a Transaction/is it best practice?

Overall code is (the extension stuff is because I'm using IIS6 and found it begins/ends even when loading images etc):

        protected void Application_BeginRequest(object sender, EventArgs args)
        {
            HttpContext context = this.Context;
            string filePath = context.Request.FilePath;
            string fileExtension = VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".aspx") || fileExtension.Equals(".ashx"))
            {
                SessionFactoryManager.Instance.BeginTransaction();
            }
        }

        protected void Application_EndRequest(object sender, EventArgs args)
        {
            HttpContext context = this.Context;
            string filePath = context.Request.FilePath;
            string fileExtension = VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".aspx") || fileExtension.Equals(".ashx"))
            {
                SessionFactoryManager.Instance.CommitTransaction();
                SessionFactoryManager.Instance.CloseSession();
            }
        }

The session manager is a singleton and the interesting bits are:

        public ISession GetSession()
        {
            ISession session = ContextSession;

            if (session == null)
            {
                session = sessionFactory.OpenSession();
                session.FlushMode = FlushMode.Commit;

                ContextSession = session;
            }

            //Check.Ensure(session != null, "session was null");

            return session;
        }

        public void BeginTransaction()
        {
            ITransaction transaction = ContextTransaction;

            if (transaction == null)
            {
                transaction = GetSession().BeginTransaction();
                ContextTransaction = transaction;
            }
        }

        public void CommitTransaction()
        {
            ITransaction transaction = ContextTransaction;

            try
            {
                if (HasOpenTransaction())
                {
                    transaction.Commit();
                    ContextTransaction = null;
                }
            }
            catch (HibernateException)
            {
                RollbackTransaction();
                throw;
            }
        }

        /// <summary>
        /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
        /// specific <see cref="CallContext" />.  Discussion concerning this found at 
        /// </summary>
        private ITransaction ContextTransaction
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
                }
                else
                {
                    return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
                }
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[TRANSACTION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(TRANSACTION_KEY, value);
                }
            }
        }

        /// <summary>
        /// If within a web context, this uses <see cref="HttpContext" /> instead of the WinForms 
        /// specific <see cref="CallContext" />.  Discussion concerning this found at 
        /// </summary>
        private ISession ContextSession
        {
            get
            {
                if (IsInWebContext())
                {
                    return (ISession)HttpContext.Current.Items[SESSION_KEY];
                }
                else
                {
                    return (ISession)CallContext.GetData(SESSION_KEY);
                }
            }
            set
            {
                if (IsInWebContext())
                {
                    HttpContext.Current.Items[SESSION_KEY] = value;
                }
                else
                {
                    CallContext.SetData(SESSION_KEY, value);
                }
            }
        }

        private bool IsInWebContext()
        {
            return HttpContext.Current != null;
        }

Thanks for any thoughts on the above, as I've been finding it really hard to track down current information regarding NHibernate and WebForms without the use of IoC frameworks to manage sessions, etc.

Thanks,

Paul

Ricardo Peres

unread,
Jun 22, 2012, 1:27:26 PM6/22/12
to nhu...@googlegroups.com
Also don't forget to dispose the session, both on EndRequest as well as on Error (if an unhandled exception occurs, the EndRequest event will not be raised).
Reply all
Reply to author
Forward
0 new messages