#region License

/*
 * Copyright © 2002-2005 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports

using System;
using System.Collections;
using Common.Logging;
using NHibernate;
using NHibernate.Connection;
using NHibernate.Driver;
using NHibernate.Engine;
using Spring.Collections;
using Spring.Context;
using Spring.Dao;
using Spring.Data.Common;
using Spring.Data.Support;
using Spring.Objects.Factory.Config;
using Spring.Threading;
using Spring.Transaction.Support;
using Spring.Util;

#endregion

namespace Spring.Data.NHibernate
{
	/// <summary>
	///  Helper class featuring methods for Hibernate Session handling,
    ///  allowing for reuse of Hibernate Session instances within transactions.
    ///  Also provides support for exception translation.
	/// </summary>
	/// <author>Mark Pollack (.NET)</author>
	public abstract class SessionFactoryUtils 
	{
		#region Fields

        /// <summary>
        /// The <see cref="ILog"/> instance for this class. 
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(typeof(SessionFactoryUtils));

		#endregion

		#region Constants

        /// <summary>
        /// The ordering value for synchronizaiton this session resources.
        /// Set to be lower than ADO.NET synchronization.
        /// </summary>
        public static readonly int SESSION_SYNCHRONIZATION_ORDER =
            AdoUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;

        private static readonly string DeferredCloseHolderDataSlotName = "Spring.Data.NHibernate:deferredCloseHolder";

		#endregion

		#region Constructor (s)
		/// <summary>
		/// Initializes a new instance of the <see cref="SessionFactoryUtils"/> class.
        /// </summary>
		public SessionFactoryUtils()
		{

		}

		#endregion

		#region Properties

		#endregion

		#region Methods

        /// <summary>
        /// Get a new Hibernate Session from the given SessionFactory.
        /// Will return a new Session even if there already is a pre-bound
        /// Session for the given SessionFactory.
        /// </summary>
        /// <remarks>
        /// Within a transaction, this method will create a new Session
        /// that shares the transaction's ADO.NET Connection. More specifically,
        /// it will use the same ADO.NET Connection as the pre-bound Hibernate Session.
        /// </remarks>
        /// <param name="sessionFactory">The session factory to create the session with.</param>
        /// <param name="interceptor">The Hibernate entity interceptor, or <code>null</code> if none.</param>
        /// <returns>The new session.</returns>
        /// <exception cref="DataAccessResourceFailureException">If could not open Hibernate session</exception>
	    public static ISession GetNewSession(ISessionFactory sessionFactory, IInterceptor interceptor)
	    {
            try 
            {
                SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
                if (sessionHolder != null && !sessionHolder.IsEmpty) 
                {
                    if (interceptor != null) 
                    {
                        return sessionFactory.OpenSession(sessionHolder.AnySession.Connection, interceptor);
                    }
                    else 
                    {
                        return sessionFactory.OpenSession(sessionHolder.AnySession.Connection);
                    }
                }
                else 
                {
                    if (interceptor != null) 
                    {
                        return sessionFactory.OpenSession(interceptor);
                    }
                    else 
                    {
                        return sessionFactory.OpenSession();
                    }
                }
            }
            catch (HibernateException ex) 
            {
                throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
            }
	    }

        /// <summary>
        /// Get a Hibernate Session for the given SessionFactory. Is aware of and will
        /// return any existing corresponding Session bound to the current thread, for
        /// example when using HibernateTransactionManager. Will always create a new
        /// Session otherwise.
        /// </summary>
        /// <remarks>
        /// Supports setting a Session-level Hibernate entity interceptor that allows
        /// to inspect and change property values before writing to and reading from the
        /// database. Such an interceptor can also be set at the SessionFactory level
        /// (i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on
        /// HibernateInterceptor/HibernateTemplate.
        /// </remarks>
        /// <param name="sessionFactory">The session factory to create the
        /// session with.</param>
        /// <param name="entityInterceptor">Hibernate entity interceptor, or <code>null</code> if none.</param>
        /// <param name="adoExceptionTranslator"> AdoExceptionTranslator to use for flushing the
        /// Session on transaction synchronization (can be <code>null</code>; only used when actually
        /// registering a transaction synchronization).</param>
        /// <returns>The Hibernate Session</returns>
        /// <exception cref="DataAccessResourceFailureException">
        /// If the session couldn't be created.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// If no thread-bound Session found and allowCreate is false.
        /// </exception>
        public static ISession GetSession(
            ISessionFactory sessionFactory, IInterceptor entityInterceptor,
            IAdoExceptionTranslator adoExceptionTranslator)
        {
            try
            {
                return GetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, true);
            }
            catch (HibernateException ex)
            {
                throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
            }
        }   

        /// <summary>
        /// Get a Hibernate Session for the given SessionFactory. Is aware of and will
        /// return any existing corresponding Session bound to the current thread, for
        /// example when using <see cref="HibernateTransactionManager"/>. Will create a new Session
        /// otherwise, if allowCreate is true.
        /// </summary>
        /// <param name="sessionFactory">The session factory to create the session with.</param>
        /// <param name="allowCreate">if set to <c>true</c> create a non-transactional Session when no
        /// transactional Session can be found for the current thread.</param>
        /// <returns>The hibernate session</returns>
        /// <exception cref="DataAccessResourceFailureException">
        /// If the session couldn't be created.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// If no thread-bound Session found and allowCreate is false.
        /// </exception>
        public static ISession GetSession(ISessionFactory sessionFactory, bool allowCreate)
        {
            try
            {
                return GetSession(sessionFactory, null, null, allowCreate);
            }
            catch (HibernateException ex)
            {
                throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
            }
        }

        /// <summary>
        /// Get a Hibernate Session for the given SessionFactory.
        /// </summary>
        /// <remarks>Is aware of and will return any existing corresponding
        /// Session bound to the current thread, for example whenusing
        /// <see cref="HibernateTransactionManager"/>.  Will create a new 
        /// Session otherwise, if "allowCreate" is true.
        /// <p>Throws the orginal HibernateException, in contrast to
        /// <see cref="GetSession(ISessionFactory, bool)"/>.
        /// </p></remarks>
        /// <param name="sessionFactory">The session factory.</param>
        /// <param name="allowCreate">if set to <c>true</c> [allow create].</param>
        /// <returns>The Hibernate Session</returns>
        /// <exception cref="HibernateException">
        /// if the Session couldn't be created
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// If no thread-bound Session found and allowCreate is false.
        /// </exception>
        public static ISession DoGetSession(ISessionFactory sessionFactory, bool allowCreate)
        {
            return DoGetSession(sessionFactory, null, null, allowCreate);
        }

        private static ISession GetSession(
            ISessionFactory sessionFactory, IInterceptor entityInterceptor,
            IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate)
        {
            try
            {
                return DoGetSession(sessionFactory, entityInterceptor, adoExceptionTranslator, allowCreate);
            }
            catch (HibernateException ex)
            {
                throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
            }
        }


        private static ISession DoGetSession(
            ISessionFactory sessionFactory, IInterceptor entityInterceptor,
            IAdoExceptionTranslator adoExceptionTranslator, bool allowCreate)
        {
            AssertUtils.ArgumentNotNull(sessionFactory, "sessionFactory", "SessionFactory can not be null");

            SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
            if (sessionHolder != null && !sessionHolder.IsEmpty) 
            {
                // pre-bound Hibernate Session
                ISession session = null;
                if (TransactionSynchronizationManager.SynchronizationActive &&
                    sessionHolder.DoesNotHoldNonDefaultSession)
                {
                    // Spring transaction management is active ->
                    // register pre-bound Session with it for transactional flushing.
                    session = sessionHolder.ValidatedSession;
                    if (!sessionHolder.SynchronizedWithTransaction) 
                    {
                        log.Debug("Registering Spring transaction synchronization for existing Hibernate Session");
                        TransactionSynchronizationManager.RegisterSynchronization(
                            new SpringSessionSynchronization(sessionHolder, sessionFactory, adoExceptionTranslator, false));
                        sessionHolder.SynchronizedWithTransaction = true;
                        // Switch to FlushMode.AUTO if we're not within a read-only transaction.
                        FlushMode flushMode = session.FlushMode;
                        if (FlushMode.Never == flushMode &&
                            !TransactionSynchronizationManager.CurrentTransactionReadOnly) 
                        {
                            session.FlushMode = FlushMode.Auto;
                            sessionHolder.PreviousFlushMode = flushMode;
                        }
                    }
                }
                else
                {
                   // No Spring transaction management active -> simply return default thread-bound Session, if any
                   // (possibly from OpenSessionInViewModule)
                    session = sessionHolder.ValidatedSession;
                }
                
                if (session != null) 
                {
                    return session;
                }
                
            }


            ISession sess = OpenSession(sessionFactory, entityInterceptor);
            // Set Session to FlushMode.Never if we're within a read-only transaction.
            // Use same Session for further Hibernate actions within the transaction.
            // Thread object will get removed by synchronization at transaction completion.
            if (TransactionSynchronizationManager.SynchronizationActive) 
            {
                log.Debug("Registering Spring transaction synchronization for new Hibernate Session");
                SessionHolder holderToUse = sessionHolder;
                if (holderToUse == null) 
                {
                    holderToUse = new SessionHolder(sess);
                }
                else 
                {
                    holderToUse.AddSession(sess);
                }
                if (TransactionSynchronizationManager.CurrentTransactionReadOnly) 
                {
                    sess.FlushMode = FlushMode.Never;
                }
                TransactionSynchronizationManager.RegisterSynchronization(
                    new SpringSessionSynchronization(holderToUse, sessionFactory, adoExceptionTranslator, true));
                holderToUse.SynchronizedWithTransaction = true;
                if (holderToUse != sessionHolder) 
                {
                    TransactionSynchronizationManager.BindResource(sessionFactory, holderToUse);
                }
            }

            

            // Check whether we are allowed to return the Session.
            if (!allowCreate && !IsSessionTransactional(sess, sessionFactory)) 
            {
                CloseSession(sess);
                throw new InvalidOperationException ("No Hibernate Session bound to thread, " +
                    "and configuration does not allow creation of non-transactional one here");
            }

            return sess;


        }

	    /// <summary>
	    /// Open a new Session from the factory.
	    /// </summary>
        /// <param name="sessionFactory">The session factory to create the session with.</param>
        /// <param name="entityInterceptor">Hibernate entity interceptor, or <code>null</code> if none.</param>
        /// <returns>the newly opened session</returns>
        internal static ISession OpenSession(ISessionFactory sessionFactory, IInterceptor entityInterceptor)
        {
            log.Debug("Opening Hibernate Session");
            ISession session = (
                                   (entityInterceptor != null)
                                   ? sessionFactory.OpenSession(entityInterceptor) 
                                   : sessionFactory.OpenSession()
                               );

            return session;
        }	         
   
        /// <summary>
        /// Perform the actual closing of the Hibernate Session
        /// catching and logging any cleanup exceptions thrown.
        /// </summary>
        /// <param name="session">The hibernate session to close</param>
        public static void CloseSession(ISession session) 
        {
            if (session != null) 
            {
                log.Debug("Closing Hibernate Session");
                try 
                {
                    session.Close();
                }
                catch (HibernateException ex) 
                {
                    log.Error("Could not close Hibernate Session", ex);
                }
                catch (Exception ex) 
                {
                    log.Error("Unexpected exception on closing Hibernate Session", ex);
                }
            }
        }

        /// <summary>
        /// Return whether the given Hibernate Session is transactional, that is,
        /// bound to the current thread by Spring's transaction facilities.
        /// </summary>
        /// <param name="session">The hibernate session to check</param>
        /// <param name="sessionFactory">The session factory that the session
        /// was created with, can be null.</param>
        /// <returns>
        /// 	<c>true</c> if the session transactional; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsSessionTransactional(ISession session, ISessionFactory sessionFactory) 
        {
            if (sessionFactory == null) 
            {
                return false;
            }
            SessionHolder sessionHolder =
                (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
            return (sessionHolder != null && sessionHolder.ContainsSession(session));
        }

        #endregion

        /// <summary>
        /// Convert the given HibernateException to an appropriate exception from the
        /// <code>Spring.Dao</code> hierarchy. Note that it is advisable to
        /// handle AdoException specifically by using a AdoExceptionTranslator for the
        /// underlying ADO.NET exception.
        /// </summary>
        /// <param name="ex">The Hibernate exception that occured.</param>
        /// <returns>DataAccessException instance</returns>
        public static DataAccessException ConvertHibernateAccessException(HibernateException ex)
        {
            if (ex is ADOException)
            {
                // ADOException during Hibernate access: only passed in here from custom code,
                // as HibernateTemplate etc will use AdoExceptionTranslator-based handling.
                return new HibernateAdoException("Ado Exception", (ADOException) ex);
            }
            if (ex is UnresolvableObjectException)
            {
                return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
            }
            if (ex is ObjectDeletedException)
            {
                return new InvalidDataAccessApiUsageException(ex.Message, ex);
            }
            if (ex is WrongClassException)
            {
                return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
            }
            if (ex is StaleObjectStateException)
            {
                return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
            }
            if (ex is StaleStateException)
            {
                return new HibernateOptimisticLockingFailureException((StaleStateException)ex);
            }
            if (ex is QueryException)
            {
                return new HibernateQueryException((QueryException) ex);
            }

            if (ex is PersistentObjectException)
            {
                return new InvalidDataAccessApiUsageException(ex.Message, ex);
            }
            if (ex is TransientObjectException)
            {
                return new InvalidDataAccessApiUsageException(ex.Message, ex);
            }

            if (ex is PropertyValueException)
            {
                return new DataIntegrityViolationException(ex.Message, ex);
            }
            if (ex is PersistentObjectException)
            {
                return new InvalidDataAccessApiUsageException(ex.Message, ex);
            }
            if (ex is NonUniqueResultException)
            {
                return new IncorrectResultSizeDataAccessException(ex.Message, 1);
            }
            // fallback
            return new HibernateSystemException(ex);
        }

        /// <summary>
        /// Close the given Session, created via the given factory,
        /// if it is not managed externally (i.e. not bound to the thread).
        /// </summary>
        /// <param name="session">The hibernate session to close</param>
        /// <param name="sessionFactory">The hibernate SessionFactory that
        /// the session was created with.</param>
	    public static void ReleaseSession(ISession session, ISessionFactory sessionFactory)
	    {
            if (session == null) 
            {
                return;
            }
            // Only close non-transactional Sessions.
            if (!IsSessionTransactional(session, sessionFactory)) 
            {
                CloseSessionOrRegisterDeferredClose(session, sessionFactory);
            }
	    }

        /// <summary>
        /// Close the given Session or register it for deferred close.
        /// </summary>
        /// <param name="session">The session.</param>
        /// <param name="sessionFactory">The session factory.</param>
	    internal static void CloseSessionOrRegisterDeferredClose(ISession session, ISessionFactory sessionFactory)
	    {
            IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary;
            
            if (holderDictionary != null && sessionFactory != null && holderDictionary.Contains(sessionFactory)) 
            {
                log.Debug("Registering Hibernate Session for deferred close");
                // Switch Session to FlushMode.NEVER for remaining lifetime.
                session.FlushMode = FlushMode.Never;
                Set sessions = (Set) holderDictionary[sessionFactory];
                sessions.Add(session);
            }
            else 
            {
                CloseSession(session);
            }
	    }

        /// <summary>
	    ///Initialize deferred close for the current thread and the given SessionFactory.
	    /// Sessions will not be actually closed on close calls then, but rather at a
	    /// processDeferredClose call at a finishing point (like request completion).
        /// </summary>
        /// <param name="sessionFactory">The session factory.</param>
        public static void InitDeferredClose(ISessionFactory sessionFactory) 
        {
            AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified");

            log.Debug("Initializing deferred close of Hibernate Sessions");

            IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary;
          
            if (holderDictionary == null) 
            {
                holderDictionary = new Hashtable();
                LogicalThreadContext.SetData(DeferredCloseHolderDataSlotName, holderDictionary);
            }
            holderDictionary.Add(sessionFactory, new ListSet());
        }

        /// <summary>
        /// Return if deferred close is active for the current thread
        /// and the given SessionFactory.</summary>
        /// <param name="sessionFactory">The session factory.</param>
        /// <returns>
        /// 	<c>true</c> if [is deferred close active] [the specified session factory]; otherwise, <c>false</c>.
        /// </returns>
        /// <exception cref="ArgumentNullException">If SessionFactory argument is null.</exception>
        public static bool IsDeferredCloseActive(ISessionFactory sessionFactory) 
        {
            if (sessionFactory == null)
            {
                throw new ArgumentNullException("sessionFactory", "No SessionFactory specified");
            }
            IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary;          
            return (holderDictionary != null && holderDictionary.Contains(sessionFactory));
        }

        /// <summary>
        /// Process Sessions that have been registered for deferred close
        /// for the given SessionFactory.
        /// </summary>
        /// <param name="sessionFactory">The session factory.</param>
        /// <exception cref="InvalidOperationException">If there is no session factory associated with the thread.</exception>
        public static void ProcessDeferredClose(ISessionFactory sessionFactory) 
        {
            AssertUtils.ArgumentNotNull(sessionFactory, "No SessionFactory specified");

            IDictionary holderDictionary = LogicalThreadContext.GetData(DeferredCloseHolderDataSlotName) as IDictionary;
          
            if (holderDictionary == null || !holderDictionary.Contains(sessionFactory)) 
            {
                throw new InvalidOperationException("Deferred close not active for SessionFactory [" + sessionFactory + "]");
            }
            log.Debug("Processing deferred close of Hibernate Sessions");
            Set sessions = (Set) holderDictionary[sessionFactory];
            holderDictionary.Remove(sessionFactory);
            foreach (ISession session in sessions)
            {
                CloseSession(session);
            }

            if (holderDictionary.Count == 0)
            {
                LogicalThreadContext.FreeNamedDataSlot(DeferredCloseHolderDataSlotName);
            }


        }

        /// <summary>
        /// Applies the current transaction timeout, if any, to the given
        /// criteria object
        /// </summary>
        /// <param name="criteria">The Hibernate Criteria object.</param>
        /// <param name="sessionFactory">Hibernate SessionFactory that the Criteria was created for
        /// (can be <code>null</code>).</param>
        /// <exception cref="ArgumentNullException">If criteria argument is null.</exception>
        public static void ApplyTransactionTimeout(ICriteria criteria, ISessionFactory sessionFactory) 
        {
            if (criteria == null)
            {
                throw new ArgumentNullException("criteria", "No Criteria object specified");
            }

            SessionHolder sessionHolder =
                (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
            if (sessionHolder != null && sessionHolder.HasTimeout) 
            {
                criteria.SetTimeout(sessionHolder.TimeToLiveInSeconds);
            }
        }

        /// <summary>
        /// Applies the current transaction timeout, if any, to the given
        /// Hibenrate query object.
        /// </summary>
        /// <param name="query">The Hibernate Query object.</param>
        /// <param name="sessionFactory">Hibernate SessionFactory that the Query was created for
        /// (can be <code>null</code>).</param>
        /// <exception cref="ArgumentNullException">If query argument is null.</exception>
	    public static void ApplyTransactionTimeout(IQuery query, ISessionFactory sessionFactory)
	    {	        
            if (query == null)
            {
                throw new ArgumentNullException("queryObject", "No query object specified");
            }
            if (sessionFactory != null) 
            {
                SessionHolder sessionHolder =
                    (SessionHolder) TransactionSynchronizationManager.GetResource(sessionFactory);
                if (sessionHolder != null && sessionHolder.HasTimeout) 
                {
                    query.SetTimeout(sessionHolder.TimeToLiveInSeconds);
                }
            }
	    }
        /// <summary>
        /// Gets the Spring IDbProvider given the ISessionFactory.
        /// </summary>
        /// <remarks>The matching is performed by comparing the assembly qualified
        /// name string of the hibernate Driver.ConnectionType to those in
        /// the DbProviderFactory definitions.  No connections are created
        /// in performing this comparison.</remarks>
        /// <param name="sessionFactory">The session factory.</param>
        /// <returns>The corresponding IDbProvider, null if no mapping was found.</returns>
        /// <exception cref="InvalidOperationException">If DbProviderFactory's ApplicaitonContext is not
        /// an instance of IConfigurableApplicaitonContext.</exception>
	    public static IDbProvider GetDbProvider(ISessionFactory sessionFactory)
	    {
	        ISessionFactoryImplementor sfi = sessionFactory as ISessionFactoryImplementor;
	        if (sfi != null)
	        {
	            
                ConnectionProvider cp = sfi.ConnectionProvider as ConnectionProvider;
	            if (cp != null)
	            {
                    IConfigurableApplicationContext ctx =
	                    Spring.Data.Common.DbProviderFactory.ApplicationContext as IConfigurableApplicationContext;
                    if (ctx == null)
                    {
                        throw new InvalidOperationException(
                            "Implementations of IApplicationContext must also implement IConfigurableApplicationContext");
                    }


                    DriverBase db = cp.Driver as DriverBase;
                    if (db != null)
                    {
                        Type hibCommandType = db.CreateCommand().GetType();

                        string[] providerNames = ctx.GetObjectNamesForType(typeof(DbProvider), true, false);
                        string hibCommandAQN = hibCommandType.AssemblyQualifiedName;
                        foreach (string providerName in providerNames)
                        {
                            IObjectDefinition objectdef = ctx.ObjectFactory.GetObjectDefinition(providerName);
                            ConstructorArgumentValues ctorArgs = objectdef.ConstructorArgumentValues;
                            ConstructorArgumentValues.ValueHolder vh = ctorArgs.NamedArgumentValues["dbmetadata"] as ConstructorArgumentValues.ValueHolder;
                            IObjectDefinition od = ((ObjectDefinitionHolder)vh.Value).ObjectDefinition;
                            ConstructorArgumentValues dbmdCtorArgs = od.ConstructorArgumentValues;
                            string commandType = dbmdCtorArgs.GetArgumentValue("commandType", typeof(string)).Value as string;
                            
                            if (hibCommandAQN.Equals(commandType))
                            {
                                IDbProvider prov = Spring.Data.Common.DbProviderFactory.GetDbProvider(providerName);
                                return prov;
                            }
                        }
                    }
	                else
                    {
                        log.Info("Could not derive IDbProvider from SessionFactory");
                    }
	            }
	            
	            
	        }
	        return null;
	    }

        /// <summary>
        /// Create a IAdoExceptionTranslator from the given SessionFactory.
        /// </summary>
        /// <remarks>If a corresponding IDbProvider is found, a ErrorcodeExceptionTranslator
        /// for the IDbProvider is created.  Otherwise, a FallbackException is created.</remarks>
        /// <param name="sessionFactory">The session factory to create the translator for</param>
        /// <returns>An IAdoExceptionTranslator</returns>
	    public static IAdoExceptionTranslator NewAdoExceptionTranslator(ISessionFactory sessionFactory)
	    {
            IDbProvider dbProvider = GetDbProvider(sessionFactory);
	        if (dbProvider != null)
	        {
                return new ErrorCodeExceptionTranslator(dbProvider);
	        }
	        log.Warn("Using FallbackException Translator.  Could not translate from ISessionFactory to IDbProvider");
            return new FallbackExceptionTranslator();
	        
	    }
	}
}
