-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 38 posts ]  Go to page Previous  1, 2, 3  Next
Author Message
 Post subject:
PostPosted: Sun Dec 04, 2005 7:50 pm 
Newbie

Joined: Sat Jul 02, 2005 12:42 pm
Posts: 11
Location: Manchester, UK
k-dub wrote:
This looks a lot like the ActiveRecord usage in MonoRail over at http://www.castleproject.org. Was any of the Castle code an inspiration, or is it just an inspired coincidence? I think it's an excellent way to solve this, but I'm a bit biased.


No, I'm not familiar with this but if it's similar then its maybe a good sign ! :?

_________________
- Simon

http://www.intesoft.net/


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 12, 2006 1:04 pm 
Beginner
Beginner

Joined: Fri Sep 02, 2005 12:13 pm
Posts: 44
Location: Denver, CO
I've posted a lenghty discussion at http://www.codeproject.com/useritems/NHibernateBestPractices.asp concerning NHibernate best practices with ASP.NET. My suggestions include using an HttpModule for implementing the open-session-in-view pattern so as to separate session management and "database awareness" from the presentation and business layers. If anyone has comments or suggestions...I'd love to hear them.

Billy


Top
 Profile  
 
 Post subject: I´ve created NHibernateUtil
PostPosted: Thu Apr 20, 2006 9:12 am 
Senior
Senior

Joined: Wed Sep 24, 2003 3:01 pm
Posts: 158
Location: Bragan�a Paulista - Brasil
Hi all,

I´ve created a NHibernateUtil using System.Runtime.Remoting.Messaging.CallContext (baggins recommended) based on http://www.codeproject.com/aspnet/NHibe ... ctices.asp,
but without to use HttpContext.
I would like that you check this code to verify if it´s OK, if this code
needs more items, etc.

Code:
using System;
using System.Collections.Generic;
using System.Text;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cache;
using NHibernate.Exceptions;

using System.Runtime.Remoting.Messaging;

namespace Hst.Database.NHibernate
{
    public sealed class NHibernateUtil
    {
        #region attributes

        /// <summary>
        /// sessionFactory attribute
        /// </summary>
        private static ISessionFactory sessionFactory;

        #endregion

        #region methods

        /// <summary>
        ///
        /// </summary>
        /// <param name="assembly"></param>
        public static void InitSessionFactory(String assembly)
        {
            if (sessionFactory == null)
            {
                Configuration cfg = new Configuration();

                cfg.AddAssembly(assembly);

                sessionFactory = cfg.BuildSessionFactory();
            }
        }

        /// <summary>
        /// Allows you to register an interceptor on a new session.  This may not be called if there is already
        /// an open session attached to the HttpContext.  If you have an interceptor to be used, modify
        /// the HttpModule to call this before calling BeginTransaction().
        /// </summary>
        public static void RegisterInterceptor(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session != null && session.IsOpen)
            {
                throw new CacheException("You cannot register an interceptor once a session has already been opened");
            }

            GetSession(interceptor);
        }

        public static ISession GetSession()
        {
            return GetSession(null);
        }

        /// <summary>
        /// Gets a session with or without an interceptor.  This method is not called directly; instead,
        /// it gets invoked from other public methods.
        /// </summary>
        private static ISession GetSession(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session == null)
            {
                if (interceptor != null)
                {
                    session = sessionFactory.OpenSession(interceptor);
                }
                else
                {
                    session = sessionFactory.OpenSession();
                }

                threadSession = session;
            }

            return session;
        }

        /// <summary>
        /// Closes a session
        /// </summary>
        public static void CloseSession()
        {
            ISession session = threadSession;

            threadSession = null;

            if (session != null && session.IsOpen)
            {
                session.Close();
            }
        }

        /// <summary>
        /// Begins a transaction
        /// </summary>
        public static void BeginTransaction()
        {
            ITransaction transaction = threadTransaction;

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

        /// <summary>
        /// Commits a transaction
        /// </summary>
        public static void CommitTransaction()
        {
            ITransaction transaction = threadTransaction;

            try
            {
                if (transaction != null && !transaction.WasCommitted
                    && !transaction.WasRolledBack)
                {
                    transaction.Commit();
                    threadTransaction = null;
                }
            }
            catch (HibernateException ex)
            {
                RollbackTransaction();
                throw ex;
            }
        }

        /// <summary>
        /// Rolls back a transaction
        /// </summary>
        public static void RollbackTransaction()
        {
            ITransaction transaction = threadTransaction;

            try
            {
                threadTransaction = null;

                if (transaction != null && !transaction.WasCommitted
                    && !transaction.WasRolledBack)
                {
                    transaction.Rollback();
                }
            }
            catch (HibernateException ex)
            {
                throw ex;
            }
            finally
            {
                CloseSession();
            }
        }

        /// <summary>
        /// Gets or sets the threadTransaction
        /// </summary>
        private static ITransaction threadTransaction
        {
            get
            {
                return (ITransaction)CallContext.GetData("THREAD_TRANSACTION");
            }
            set
            {
                CallContext.SetData("THREAD_TRANSACTION", value);
            }
        }

        /// <summary>
        /// Gets or sets the threadSession
        /// </summary>
        private static ISession threadSession
        {
            get
            {
                return (ISession)CallContext.GetData("THREAD_SESSION");
            }
            set
            {
                CallContext.SetData("THREAD_SESSION", value);
            }
        }

        #endregion
    }
}


So, into Global.asax (in the web for example) I put the code to initialize the static SessionFactory:

Code:
    void Application_Start(object sender, EventArgs e)
    {
        // Code that runs on application startup

Hst.Database.NHibernate.NHibernateUtil.InitSessionFactory("NHibernateSample.Core");
       
    }


and the DAOs call static methods like getSession, commitTransaction, etc.

thanks!!!!

_________________
Tads


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 20, 2006 11:25 am 
Beginner
Beginner

Joined: Fri Sep 02, 2005 12:13 pm
Posts: 44
Location: Denver, CO
Nice mod tads!

Billy


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 20, 2006 1:57 pm 
Senior
Senior

Joined: Wed Sep 24, 2003 3:01 pm
Posts: 158
Location: Bragan�a Paulista - Brasil
Thank u very much Billy!!!!

_________________
Tads


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 24, 2006 3:25 pm 
Senior
Senior

Joined: Wed Sep 24, 2003 3:01 pm
Posts: 158
Location: Bragan�a Paulista - Brasil
Hi all again,

I´ve updated the code of HibernateUtil to use TransactionScope too
based on http://forum.hibernate.org/viewtopic.php?t=957900 by kpatton.

Code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cache;
using NHibernate.Exceptions;

using System.Runtime.Remoting.Messaging;

namespace Company.Database.NHibernate
{
    public sealed class NHibernateUtil
    {
        #region attributes

        /// <summary>
        /// sessionFactory attribute
        /// </summary>
        private static ISessionFactory sessionFactory;

        #endregion

        #region general methods

        /// <summary>
        ///
        /// </summary>
        /// <param name="assembly"></param>
        public static void InitSessionFactory(String assembly)
        {
            if (sessionFactory == null)
            {
                Configuration cfg = new Configuration();

                cfg.AddAssembly(assembly);

                sessionFactory = cfg.BuildSessionFactory();
            }
        }

        /// <summary>
        /// Allows you to register an interceptor on a new session.  This may not be called if there is already
        /// an open session attached to the HttpContext.  If you have an interceptor to be used, modify
        /// the HttpModule to call this before calling BeginTransaction().
        /// </summary>
        public static void RegisterInterceptor(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session != null && session.IsOpen)
            {
                throw new CacheException("You cannot register an interceptor once a session has already been opened");
            }

            GetSession(interceptor);
        }

        public static ISession GetSession()
        {
            return GetSession(null);
        }

        /// <summary>
        /// Gets a session with or without an interceptor.  This method is not called directly; instead,
        /// it gets invoked from other public methods.
        /// </summary>
        private static ISession GetSession(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session == null)
            {
                if (interceptor != null)
                {
                    session = sessionFactory.OpenSession(interceptor);
                }
                else
                {
                    session = sessionFactory.OpenSession();
                }

                threadSession = session;
            }

            return session;
        }

        /// <summary>
        /// Closes a session
        /// </summary>
        public static void CloseSession()
        {
            ISession session = threadSession;

            threadSession = null;

            if (session != null && session.IsOpen)
            {
                session.Close();
            }
        }

        /// <summary>
        /// Begins a transaction
        /// </summary>
        public static void BeginTransaction()
        {
            ITransaction transaction = threadTransaction;

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

        /// <summary>
        /// Commits a transaction
        /// </summary>
        public static void CommitTransaction()
        {
            ITransaction transaction = threadTransaction;

            try
            {
                if (transaction != null && !transaction.WasCommitted
                    && !transaction.WasRolledBack)
                {
                    transaction.Commit();
                    threadTransaction = null;
                }
            }
            catch (HibernateException ex)
            {
                RollbackTransaction();
                throw ex;
            }
        }

        /// <summary>
        /// Rolls back a transaction
        /// </summary>
        public static void RollbackTransaction()
        {
            ITransaction transaction = threadTransaction;

            try
            {
                threadTransaction = null;

                if (transaction != null && !transaction.WasCommitted
                    && !transaction.WasRolledBack)
                {
                    transaction.Rollback();
                }
            }
            catch (HibernateException ex)
            {
                throw ex;
            }
            finally
            {
                CloseSession();
            }
        }

        /// <summary>
        /// Gets or sets the threadTransaction
        /// </summary>
        private static ITransaction threadTransaction
        {
            get
            {
                return (ITransaction)CallContext.GetData("THREAD_TRANSACTION");
            }
            set
            {
                CallContext.SetData("THREAD_TRANSACTION", value);
            }
        }

        /// <summary>
        /// Gets or sets the threadSession
        /// </summary>
        private static ISession threadSession
        {
            get
            {
                return (ISession)CallContext.GetData("THREAD_SESSION");
            }
            set
            {
                CallContext.SetData("THREAD_SESSION", value);
            }
        }

        #endregion

        #region Transaction Scope

        /// <summary>
        /// Gets new trasanction scope
        /// </summary>
        /// <param name="scopeOption"></param>
        /// <returns></returns>
        public static TransactionScope GetNewScope(TransactionScopeOption scopeOption)
        {
            bool newTransaction = System.Transactions.Transaction.Current == null;

            if (newTransaction)
            {
                DisconnectCurrentSession();
            }

            TransactionScope scope = new TransactionScope(scopeOption);

            if (newTransaction)
            {
                ReconnectCurrentSession();
            }
           
            return scope;
        }

        /// <summary>
        /// disconnects the current session (if not in active transaction)
        /// </summary>
        public static void DisconnectCurrentSession()
        {
            ISession session = GetSession();

            if (session == null) return;

            if (!session.IsConnected) return;

            if (session.Transaction == null)
            {
                session.Disconnect();
            }
            else
            {
                if (session.Transaction.WasCommitted || session.Transaction.WasRolledBack)
                {
                    session.Disconnect();
                }
            }
        }

        /// <summary>
        /// disconnects the current session (if not already connected)
        /// </summary>
        public static void ReconnectCurrentSession()
        {
            ISession session = GetSession();

            if (session == null)
            {
                return;
            }

            if (!session.IsConnected)
            {
                session.Reconnect();
            }
        }

        #endregion
    }
}



What do you think?

I´m testing the HibernateUtil with TransactionScope with some DAO´s and Business Objects
and it´s OK... If you found some problems... :)

For example, I´ve created the BO like this:

Code:
using System;
using System.Collections;
using System.Text;
using System.Transactions;
using NHibernateSample.Core.Domain;
using NHibernateSample.Dao;
using NHibernateSample.Dao.NHibernate;

using log4net;
[assembly: log4net.Config.XmlConfigurator(Watch = true)]

namespace NHibernateSample.Bo
{
    public class CustomerBO
    {
        #region attributes

        /// <summary>
        /// customerDAO attribute
        /// </summary>
        private CustomerDAO customerDAO = new NHibernateCustomerDAO();

        /// <summary>
        /// userLogBO attribute
        /// </summary>
      private UserLogBO userLogBO = new UserLogBO();

        /// <summary>
        /// Log4Net attribute
        /// </summary>
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger((System.Reflection.MethodBase.GetCurrentMethod().DeclaringType));

        #endregion

        #region methods

        /// <summary>
        /// Inserts a customer
        /// </summary>
        /// <param name="customer"></param>
        public void Insert(Customer customer)
        {
            using (TransactionScope scope = Company.Database.NHibernate.NHibernateUtil.GetNewScope(TransactionScopeOption.Required))
            {
                try
                {
                    customerDAO.Insert(customer);

               // business Logic to insert log into DB
               // Using other BO that contains TransactionScope too:
               UserLogTO userLog = new UserLogTO();
               userLog.User = customer.LoggedUser;

               ActionTO action = new ActionTO();
               action.Id = "FF";

               userLog.Action = action;

                    userLogBO.Insert(userLog); // into this method contains Transaction scope too
               // if the above Insert fails, it´s rolled back the transaction

                    Company.Database.NHibernate.NHibernateUtil.CommitTransaction();
                }
                catch (Exception e)
                {
                    Company.Database.NHibernate.NHibernateUtil.RollbackTransaction();

                    log.Error("Method Insert: ", e);

                    throw e;
                }
                finally
                {
                    Company.Database.NHibernate.NHibernateUtil.CloseSession();

                    scope.Complete();
                }
            }
        }
    }
}


Thanks,

Tads

_________________
Tads


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 24, 2006 3:29 pm 
Beginner
Beginner

Joined: Fri Sep 02, 2005 12:13 pm
Posts: 44
Location: Denver, CO
Wow, I'll be sure to send you more of my code samples to spice them all up. Very nice work! I'll be sure to add a comment to the end of my article talking about this.

Billy


Top
 Profile  
 
 Post subject:
PostPosted: Mon Apr 24, 2006 3:39 pm 
Beginner
Beginner

Joined: Fri Sep 02, 2005 12:13 pm
Posts: 44
Location: Denver, CO
The only thing I'd modify on this is to simplify it so that you could simply add an attribute to the method such as
Code:
[Transactional]
or something like that. This would be similar to how the Castle Project does their NHibernate transaction management: http://www.castleproject.org/index.php/Facility:NHibernate. As a bonus, it would be non-NHibernate specific so it wouldn't be concretely tied to NHibernate at all. But what you've done is a great step in that direction.

Billy


Top
 Profile  
 
 Post subject: Great mods
PostPosted: Thu Apr 27, 2006 12:42 pm 
Newbie

Joined: Thu Feb 09, 2006 3:03 pm
Posts: 6
I haven't dipped my toe into System.Transactions yet, but I like the use of System.Runtime.Remoting.Messaging.CallContext. By removing the dependency on HttpContext, you've simplified my code. I no longer have to check for HttpContext.Context == null and store the ISession or ITransaction in a local static, which was what I was doing to get my NUnit tests to run. So can use my classes in NUnit, WinForms, whatever. Nice!

I'd love to hear from anyone else using this technique as to whether it's "solid".

Larry


Top
 Profile  
 
 Post subject:
PostPosted: Thu Apr 27, 2006 1:05 pm 
Beginner
Beginner

Joined: Fri Sep 02, 2005 12:13 pm
Posts: 44
Location: Denver, CO
I've thouroughly tested Tads recommendation of using CallContext instead of HttpContext.Current.Items and it works like a champ. I no longer need an HttpContext simulator for my unit tests and the ASPX pages has been unaffected by the change. I'm currently updating the NHibernate Best Practices article to reflect his suggestion. I haven't implemented his transactional support yet, but am looking foward to trying that out as well.

Billy


Top
 Profile  
 
 Post subject:
PostPosted: Thu May 04, 2006 8:17 am 
Senior
Senior

Joined: Wed Sep 24, 2003 3:01 pm
Posts: 158
Location: Bragan�a Paulista - Brasil
Thanks for your post Billy.

I´m learning so much about NHibernate here.

_________________
Tads


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 1:22 pm 
Pro
Pro

Joined: Fri Nov 19, 2004 5:52 pm
Posts: 232
Location: Chicago, IL
Quote:
ASP.NET 2.0 allows a single web request to switch threads, or for one thread to switch between requests before the first request is finished. If this is enabled you should never use TLS because your sessions will get mixed up.


aarnott,

Do you have a link to official Microsoft documentation stating this? I find it hard to believe that a single request would be suspended on one thread and then resumed on another. You may be right, I'd just like to see where in Microsoft's documentation it states this.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 2:26 pm 
Regular
Regular

Joined: Wed Jun 21, 2006 3:13 pm
Posts: 110
So, being curious, I went digging.

I think it's been (mis?)said since the dawn of ASP.NET that it wasn't safe to use TLS because multiple threads could service your request. I think it's more correct to state that you should only use TLS for data you know will be bound to that thread and the lifecycle of that thread.

My basis for this is doing a little digging with Reflector and the System.Web dll. If you look, HttpContext.Current is a wrapper to CallContext:

Code:
public static HttpContext get_Current()
{
  return (CallContext.GetData("HttpContext") as HttpContext);
}


CallContext.GetData looks like this:
Code:
public static object GetData(string name)
{
      object obj1 = CallContext.LogicalGetData(name);
      if (obj1 == null)
      {
            return CallContext.IllogicalGetData(name);
      }
      return obj1;
}


LogicalGetData:
Code:
private static object LogicalGetData(string name)
{
      return Thread.CurrentThread.GetLogicalCallContext().GetData(name);
}


Thread's GetData:
Code:
public object GetData(string name)
{
      return this.Datastore[name];
}


So, in summary, I think that Microsoft relies on TLS to handle HttpContext.Current (unless I'm reading this wrong). So, it would seem it's save to use TLS.

That said, I personally still like to use CallContext as my entry point for most things... call me new-fashioned I guess.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 2:27 pm 
Regular
Regular

Joined: Fri May 13, 2005 4:08 pm
Posts: 64
aarnott wrote:
ASP.NET 2.0 allows a single web request to switch threads, or for one thread to switch between requests before the first request is finished. If this is enabled you should never use TLS because your sessions will get mixed up.

jemiller wrote:
Do you have a link to official Microsoft documentation stating this? I find it hard to believe that a single request would be suspended on one thread and then resumed on another. You may be right, I'd just like to see where in Microsoft's documentation it states this.


Yes, I have: http://msdn.microsoft.com/msdnmag/issues/06/07/WebAppFollies/default.aspx

This passing of web requests between threads may be confined to the ASP.NET 2.0 feature of asynchronous page requests, which is a little known but very useful feature. It's documented at this link, and here is the relevant snippet:
MSDN wrote:
... A request for an asynchronous page begins its life on one thread, but returns that thread and an IAsyncResult interface to ASP.NET when it begins an I/O operation. When the operation completes, the request signals ASP.NET through IAsyncResult and ASP.NET pulls another thread from the pool and finishes processing the request. Significantly, no thread pool threads are consumed while the I/O operation takes place. This can dramatically improve throughput by preventing requests for other pages—pages that don’t perform lengthy I/O operations—from waiting in a queue.
[/url]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jun 26, 2006 3:22 pm 
Pro
Pro

Joined: Fri Nov 19, 2004 5:52 pm
Posts: 232
Location: Chicago, IL
Thanks for the info. It looks like you only run into this problem if you are using asynchronous pages. Thanks for clarifying that. That seems to make sense. It looks like it just works like normal asynchronous I/O does. So, it makes sense that there would be more than one thread. i.e. you get a callback on another thread when the operation has completed.

I'm not using asynchronous pages, so, for the time being, I think I'll just stick with using a ThreadStatic because I want my helper class to be usable with or without ASP.NET.

Here's some more information on asynchronous pages in case anyone is interested.

http://msdn.microsoft.com/msdnmag/issue ... ickedCode/


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 38 posts ]  Go to page Previous  1, 2, 3  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.