#region License

/*
 * Copyright © 2002-2006 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

#if NET_2_0


using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using Common.Logging;
using Spring.Dao;
using Spring.Dao.Support.Generic;
using Spring.Data.Common;
using Spring.Data.Core;
using Spring.Data.Support;
using Spring.Util;

namespace Spring.Data.Generic
{
    /// <summary>
    /// This is the central class in the Spring.Data.Generic namespace.
    /// It simplifies the use of ADO.NET and helps to avoid commons errors.
    /// </summary>
    /// <author>Mark Pollack (.NET)</author>
    public class AdoTemplate : AdoAccessor, IAdoOperations
    {

        #region Logging Definition

        private static readonly ILog LOG = LogManager.GetLogger(typeof(AdoTemplate));

        #endregion

        #region Fields

        private Core.AdoTemplate classicAdoTemplate;
        protected Type dataReaderWrapperType;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="AdoTemplate"/> class.
        /// </summary>
        public AdoTemplate()
            : base()
        {
            classicAdoTemplate = new Core.AdoTemplate();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AdoTemplate"/> class.
        /// </summary>
        /// <param name="provider">The provider.</param>
        public AdoTemplate(IDbProvider provider)
        {
            classicAdoTemplate = new Core.AdoTemplate(provider);
            AfterPropertiesSet();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AdoTemplate"/> class.
        /// </summary>
        /// <param name="provider">The database provider.</param>
        /// <param name="lazyInit">if set to <c>false</c>
        /// lazily initialize the ErrorCodeExceptionTranslator.</param>
        public AdoTemplate(IDbProvider provider, bool lazyInit)
        {
            classicAdoTemplate = new Core.AdoTemplate(provider, lazyInit);
            AfterPropertiesSet();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AdoTemplate"/> class.
        /// </summary>
        /// <remarks>Useful if you have a subclass of Spring.Data.Core.AdoTemplate.</remarks>
        /// <param name="classicAdoTemplate">The classic ADO template.</param>
        public AdoTemplate(Core.AdoTemplate classicAdoTemplate)
        {
            this.classicAdoTemplate = classicAdoTemplate;
            AfterPropertiesSet();
        }
        #endregion

        #region Properties

        /// <summary>
        /// An instance of a DbProvider implementation.
        /// </summary>
        public override IDbProvider DbProvider
        {
            get { return classicAdoTemplate.DbProvider; }
            set { classicAdoTemplate.DbProvider = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether to lazily initialize the 
        /// IAdoExceptionTranslator for this accessor, on first encounter of a 
        /// exception from the data provider.  Default is "true"; can be switched to
        /// "false" for initialization on startup.
        /// </summary>
        /// <value><c>true</c> if to lazy initialize the IAdoExceptionTranslator; 
        /// otherwise, <c>false</c>.</value>
        public override bool LazyInit
        {
            get { return classicAdoTemplate.LazyInit; }
            set { classicAdoTemplate.LazyInit = value; }
        }

        /// <summary>
        /// Gets or sets the exception translator. If no custom translator is provided, a default
        /// <see cref="ErrorCodeExceptionTranslator"/> is used.
        /// </summary>
        /// <value>The exception translator.</value>
        public override IAdoExceptionTranslator ExceptionTranslator
        {
            get { return classicAdoTemplate.ExceptionTranslator; }
            set { classicAdoTemplate.ExceptionTranslator = value; }
        }


        /// <summary>
        /// Gets or set the System.Type to use to create an instance of IDataReaderWrapper 
        /// for the purpose of having defaults values to use in case of DBNull values read 
        /// from IDataReader.
        /// </summary>
        /// <value>The type of the data reader wrapper.</value>
        public override Type DataReaderWrapperType
        {
            get { return classicAdoTemplate.DataReaderWrapperType; }
            set { classicAdoTemplate.DataReaderWrapperType = value; }
        }

        /// <summary>
        /// Gets the 'Classic' AdoTemplate in Spring.Data
        /// </summary>
        /// <remarks>
        /// Useful to access methods that return non-generic collections.
        /// </remarks>
        /// <value>The 'Classic; AdoTemplate in Spring.Data.</value>
        public Core.AdoTemplate ClassicAdoTemplate
        {
            get { return classicAdoTemplate; }
        }

        /// <summary>
        /// Gets or sets the command timeout for IDbCommands that this AdoTemplate executes.  It also
        /// sets the value of the contained 'ClassicAdoTemplate'.
        /// </summary>
        /// <value>The command timeout.</value>
        /// <remarks>Default is 0, indicating to use the database provider's default.
        /// Any timeout specified here will be overridden by the remaining
        /// transaction timeout when executing within a transaction that has a
        /// timeout specified at the transaction level.
        /// </remarks>
        public override int CommandTimeout
        {
            get { return commandTimeout; }
            set
            {
                commandTimeout = value;
                classicAdoTemplate.CommandTimeout = value;
            }
        }

        #endregion

        #region General Execute Callback methods

        /// <summary>
        /// Execute a ADO.NET operation on a command object using a interface based callback.
        /// </summary>
        /// <param name="action">the callback to execute</param>
        /// <returns>object returned from callback</returns>
        public T Execute<T>(ICommandCallback<T> action)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);

            IDbCommand command = null;
            try
            {
                command = DbProvider.CreateCommand();
                command.Connection = connectionTxPairToUse.Connection;
                command.Transaction = connectionTxPairToUse.Transaction;
                //TODO collect warnings...
                //RegisterEventHandlers(command.Connection);
                ApplyCommandSettings(command);
                //TODO explicit check to cast to base class.
                DbCommand commandToUse = command as DbCommand;
                if (commandToUse != null)
                {
                    T result = action.DoInCommand((DbCommand)command);
                    //SqlWarnings sqlWarnings = GetSqlWarnings()
                    //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings);
                    return result;
                }
                else
                {
                    throw new InvalidDataAccessApiUsageException(
                        "Providers implementation of IDbCommand does not inherit from System.Data.Common.DbCommand.  Use the alternative overloaded Execute method that specifies IDbCommandCallback as a parameter.");
                }

            }
            catch (Exception e)
            {
                AdoUtils.DisposeCommand(command);
                command = null;
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                if (DbProvider.IsDataAccessException(e))
                {
                    throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e);
                }
                else
                {
                    throw;
                }


            }
            finally
            {
                AdoUtils.DisposeCommand(command);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }
        }

        /// <summary>
        /// Execute a ADO.NET operation on a command object using a delegate callback.
        /// </summary>
        /// <remarks>This allows for implementing arbitrary data access operations
        /// on a single command within Spring's managed ADO.NET environment.</remarks>
        /// <param name="del">The delegate called with a command object.</param>
        /// <returns>A result object returned by the action or null</returns>
        public T Execute<T>(CommandDelegate<T> del)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);

            IDbCommand command = null;
            try
            {
                command = DbProvider.CreateCommand();
                command.Connection = connectionTxPairToUse.Connection;
                command.Transaction = connectionTxPairToUse.Transaction;
                //TODO collect warnings...
                //RegisterEventHandlers(command.Connection);
                ApplyCommandSettings(command);
                DbCommand commandToUse = command as DbCommand;
                if (commandToUse != null)
                {
                    T result = del((DbCommand) command);
                    //SqlWarnings sqlWarnings = GetSqlWarnings()
                    //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings);
                    return result;
                }
                else
                {
                    throw new InvalidDataAccessApiUsageException(
                        "Providers implementation of IDbCommand does not inherit from System.Data.Common.DbCommand.  Use the alternative overloaded Execute method that specifies IDbCommandDelegate as a parameter.");
                }            
            }
            catch (Exception e)
            {
                string commandText = command.CommandText;
                AdoUtils.DisposeCommand(command);
                command = null;
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                if (DbProvider.IsDataAccessException(e))
                {
                    throw ExceptionTranslator.Translate("CommandCallback", commandText, e);
                }
                else
                {
                    throw;
                }


            }
            finally
            {
                AdoUtils.DisposeCommand(command);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }
        }


        /// <summary>
        /// Execute a ADO.NET operation on a command object using a interface based callback.
        /// </summary>
        /// <param name="action">the callback to execute</param>
        /// <returns>object returned from callback</returns>
        public T Execute<T>(IDbCommandCallback<T> action)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);

            IDbCommand command = null;
            try
            {
                command = DbProvider.CreateCommand();
                command.Connection = connectionTxPairToUse.Connection;
                command.Transaction = connectionTxPairToUse.Transaction;
                //TODO collect warnings...
                //RegisterEventHandlers(command.Connection);
                ApplyCommandSettings(command);
                T result = action.DoInCommand(command);
                //SqlWarnings sqlWarnings = GetSqlWarnings()
                //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings);
                return result;
            }
            catch (Exception e)
            {
                AdoUtils.DisposeCommand(command);
                command = null;
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                if (DbProvider.IsDataAccessException(e))
                {
                    throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e);
                }
                else
                {
                    throw;
                }


            }
            finally
            {
                AdoUtils.DisposeCommand(command);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }
        }

        /// <summary>
        /// Execute a ADO.NET operation on a command object using a delegate callback.
        /// </summary>
        /// <remarks>This allows for implementing arbitrary data access operations
        /// on a single command within Spring's managed ADO.NET environment.</remarks>
        /// <param name="del">The delegate called with a command object.</param>
        /// <returns>A result object returned by the action or null</returns>
        public T Execute<T>(IDbCommandDelegate<T> del)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);

            IDbCommand command = null;
            try
            {
                command = DbProvider.CreateCommand();
                command.Connection = connectionTxPairToUse.Connection;
                command.Transaction = connectionTxPairToUse.Transaction;
                //TODO collect warnings...
                //RegisterEventHandlers(command.Connection);
                ApplyCommandSettings(command);
                T result = del(command);
                //SqlWarnings sqlWarnings = GetSqlWarnings()
                //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings);
                return result;
            }
            catch (Exception e)
            {
                string commandText = command.CommandText;
                AdoUtils.DisposeCommand(command);
                command = null;
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                if (DbProvider.IsDataAccessException(e))
                {
                    throw ExceptionTranslator.Translate("CommandCallback", commandText, e);
                }
                else
                {
                    throw;
                }


            }
            finally
            {
                AdoUtils.DisposeCommand(command);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }
        }


        /// <summary>
        /// Executes ADO.NET operations on a command object, created by the provided IDbCommandCreator,
        /// using the interface based callback IDbCommandCallback.
        /// </summary>
        /// <typeparam name="T">The type of object returned from the callback.</typeparam>
        /// <param name="commandCreator">The command creator.</param>
        /// <param name="action">The callback to execute based on IDbCommand</param>
        /// <returns>
        /// A result object returned by the action or null
        /// </returns>
        public T Execute<T>(IDbCommandCreator commandCreator, IDbCommandCallback<T> action)
        {
            AssertUtils.ArgumentNotNull(commandCreator, "commandCreator", "IDbCommandCreator must not be null");
            AssertUtils.ArgumentNotNull(action, "action", "Callback object must not be null");
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);

            IDbCommand command = null;
            try
            {
                command = commandCreator.CreateDbCommand(DbProvider);
                command.Connection = connectionTxPairToUse.Connection;
                command.Transaction = connectionTxPairToUse.Transaction;
                //TODO collect warnings...
                //RegisterEventHandlers(command.Connection);
                ApplyCommandSettings(command);
                T result = action.DoInCommand(command);
                //SqlWarnings sqlWarnings = GetSqlWarnings()
                //ThrowExceptionOnWarningIfNotIgnoringWarnings(sqlWarnings);
                return result;
            }
            catch (Exception e)
            {
                AdoUtils.DisposeCommand(command);
                command = null;
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                if (DbProvider.IsDataAccessException(e))
                {
                    throw ExceptionTranslator.Translate("CommandCallback", GetCommandText(action), e);
                }
                else
                {
                    throw;
                }


            }
            finally
            {
                AdoUtils.DisposeCommand(command);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }

        }

        /// <summary>
        /// Execute ADO.NET operations on a IDbDataAdapter object using an interface based callback.
        /// </summary>
        /// <typeparam name="T">The type of object returned from the callback.</typeparam>
        /// <param name="dataAdapterCallback">The data adapter callback.</param>
        /// <returns>
        /// A result object returned by the callback or null
        /// </returns>
        /// <remarks>This allows for implementing abritrary data access operations
        /// on a single DataAdapter within Spring's managed ADO.NET environment.
        /// </remarks>
        public T Execute<T>(IDataAdapterCallback<T> dataAdapterCallback)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);
            IDbDataAdapter dataAdapter = null;
            try
            {
                dataAdapter = DbProvider.CreateDataAdapter();
                //TODO row updated event handling...
                dataAdapter.SelectCommand = DbProvider.CreateCommand();
                dataAdapter.SelectCommand.Connection = connectionTxPairToUse.Connection;
                //TODO register for warnings on connection.
                dataAdapter.SelectCommand.Transaction = connectionTxPairToUse.Transaction;
                ApplyCommandSettings(dataAdapter.SelectCommand);
                T result = dataAdapterCallback.DoInDataAdapter(dataAdapter);
                return result;
            }
            catch (Exception)
            {
                AdoUtils.DisposeDataAdapterCommands(dataAdapter);
                //TODO set dataAdapter command's = null; ?
                //TODO exception translation? different hierarchy for data set operations.
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                throw;
            }
            finally
            {
                AdoUtils.DisposeDataAdapterCommands(dataAdapter);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }


        }

        /// <summary>
        /// Execute ADO.NET operations on a IDbDataAdapter object using an delgate based callback.
        /// </summary>
        /// <remarks>This allows for implementing abritrary data access operations
        /// on a single DataAdapter within Spring's managed ADO.NET environment.
        /// </remarks>        
        /// <typeparam name="T">The type of object returned from the callback.</typeparam>
        /// <param name="dataAdapterCallback">The delegate called with a IDbDataAdapter object.</param>
        /// <returns>A result object returned by the callback or null</returns>
        public T Execute<T>(DataAdapterDelegate<T> dataAdapterCallback)
        {
            ConnectionTxPair connectionTxPairToUse = ConnectionUtils.GetConnectionTxPair(DbProvider);
            IDbDataAdapter dataAdapter = null;
            try
            {
                dataAdapter = DbProvider.CreateDataAdapter();
                //TODO row updated event handling...
                dataAdapter.SelectCommand = DbProvider.CreateCommand();
                dataAdapter.SelectCommand.Connection = connectionTxPairToUse.Connection;
                //TODO register for warnings on connection.
                dataAdapter.SelectCommand.Transaction = connectionTxPairToUse.Transaction;
                ApplyCommandSettings(dataAdapter.SelectCommand);
                T result = dataAdapterCallback(dataAdapter);
                return result;

            }
            catch (Exception)
            {
                AdoUtils.DisposeDataAdapterCommands(dataAdapter);
                //TODO set dataAdapter command's = null; ?
                //TODO exception translation? different hierarchy for data set operations.
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
                connectionTxPairToUse.Connection = null;
                throw;
            }
            finally
            {
                AdoUtils.DisposeDataAdapterCommands(dataAdapter);
                ConnectionUtils.DisposeConnection(connectionTxPairToUse.Connection, DbProvider);
            }
        }

        #endregion

        #region ExecuteNonQuery

        /// <summary>
        /// Executes a non query returning the number of rows affected.
        /// </summary>
        /// <param name="cmdType">The command type.</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery(CommandType cmdType, string cmdText)
        {
            return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText);
        }


        /// <summary>
        /// Executes a non query returning the number of rows affected.
        /// </summary>
        /// <param name="cmdType">The command type.</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="parameterName">The name of the parameter to map.</param>
        /// <param name="dbType">One of the database parameter type enumerations.</param>
        /// <param name="size">The length of the parameter. 0 if not applicable to parameter type.</param>
        /// <param name="parameterValue">The parameter value.</param>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery(CommandType cmdType, string cmdText,
                                   string parameterName, Enum dbType, int size, object parameterValue)
        {
            return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, parameterName, dbType, size, parameterValue);


        }

        /// <summary>
        /// Executes a non query returning the number of rows affected.
        /// </summary>
        /// <param name="cmdType">The command type.</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="parameters">The parameter collection to map.</param>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery(CommandType cmdType, string cmdText,
                                   IDbParameters parameters)
        {
            return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, parameters);

        }

        /// <summary>
        /// Executes a non query with parameters set via the
        /// command setter, returning the number of rows affected.
        /// </summary>
        /// <param name="cmdType">The command type.</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="commandSetter">The command setter.</param>
        /// <returns>The number of rows affected.</returns>
        public int ExecuteNonQuery(CommandType cmdType, string cmdText,
                                   ICommandSetter commandSetter)
        {
            return classicAdoTemplate.ExecuteNonQuery(cmdType, cmdText, commandSetter);
        }

        /// <summary>
        /// Executes a non query with a command created via IDbCommandCreator and
        /// parameters.
        /// </summary>
        /// <remarks>Output parameters can be retrieved via the returned
        /// dictionary.
        /// <para>
        /// More commonly used as a lower level support method within the framework,
        /// for example StoredProcedure/AdoScalar.
        /// </para>
        /// </remarks>
        /// <param name="commandCreator">The callback to create a IDbCommand.</param>
        /// <returns>The number of rows affected.</returns>
        public IDictionary ExecuteNonQuery(IDbCommandCreator commandCreator)
        {
            return classicAdoTemplate.ExecuteNonQuery(commandCreator);
        }

        #endregion

        #region ExecuteScalar

        /// <summary>
        /// Execute the query with the specified command text.
        /// </summary>
        /// <remarks>No parameters are used.  As with 
        /// IDbCommand.ExecuteScalar, it returns the first column of the first row in the result set
        /// returned by the query.  Extra columns or row are ignored.</remarks>
        /// <param name="cmdType">The command type</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <returns>The first column of the first row in the result set</returns>
        public object ExecuteScalar(CommandType cmdType, string cmdText)
        {
            return classicAdoTemplate.ExecuteScalar(cmdType, cmdText);
        }

        /// <summary>
        /// Execute the query with the specified command text and parameter returning a scalar result
        /// </summary>
        /// <param name="cmdType">The command type</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="parameterName">The name of the parameter to map.</param>
        /// <param name="dbType">One of the database parameter type enumerations.</param>
        /// <param name="size">The length of the parameter. 0 if not applicable to parameter type.</param>
        /// <param name="parameterValue">The parameter value.</param>
        /// <returns>The first column of the first row in the result set</returns>
        public object ExecuteScalar(CommandType cmdType, string cmdText,
                                    string parameterName, Enum dbType, int size, object parameterValue)
        {
            return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, parameterName, dbType, size, parameterValue);

        }


        /// <summary>
        /// Execute the query with the specified command text and parameters returning a scalar result
        /// </summary>
        /// <param name="cmdType">The command type</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="parameters">The parameter collection to map.</param>
        /// <returns>The first column of the first row in the result set</returns>
        public object ExecuteScalar(CommandType cmdType, string cmdText,
                                    IDbParameters parameters)
        {
            return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, parameters);
        }

        /// <summary>
        /// Execute the query with the specified command text and parameters set via the
        /// command setter, returning a scalar result
        /// </summary>
        /// <param name="cmdType">The command type</param>
        /// <param name="cmdText">The command text to execute.</param>
        /// <param name="commandSetter">The command setter.</param>
        /// <returns>The first column of the first row in the result set</returns>
        public object ExecuteScalar(CommandType cmdType, string cmdText,
                                    ICommandSetter commandSetter)
        {
            return classicAdoTemplate.ExecuteScalar(cmdType, cmdText, commandSetter);
        }

        /// <summary>
        /// Execute the query with a command created via IDbCommandCreator and 
        /// parameters
        /// </summary>
        /// <remarks>Output parameters can be retrieved via the returned
        /// dictionary.
        /// <para>
        /// More commonly used as a lower level support method within the framework,
        /// for example for StoredProcedure/AdoScalar.
        /// </para></remarks>
        /// <param name="commandCreator">The callback to create a IDbCommand.</param>
        /// <returns>A dictionary containing output parameters, if any</returns>
        public IDictionary ExecuteScalar(IDbCommandCreator commandCreator)
        {
            return classicAdoTemplate.ExecuteScalar(commandCreator);
        }

        #endregion

        #region Queries with RowCallback

        /// <summary>
        /// Execute a query given IDbCommand's type and text by 
        /// passing the created IDbCommand to a ICommandSetter implementation 
        /// that knows how to bind values to the IDbCommand, reading a 
        /// single result set on a per-row basis with a <see cref="IRowCallback"/>.
        /// </summary>
        /// <param name="cmdType">The type of command</param>
        /// <param name="cmdText">The text of the query.</param>
        /// <param name="rowCallback">callback that will extract results
        /// one row at a time.
        /// </param>
        /// <param name="commandSetter">The command setter.</param>
        public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback,
                                         ICommandSetter commandSetter)
        {
            classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback, commandSetter);
        }
        /// <summary>
        /// Execute a query given IDbCommand's type and text, reading a 
        /// single result set on a per-row basis with a <see cref="IRowCallback"/>.
        /// </summary>
        /// <param name="cmdType">The type of command</param>
        /// <param name="cmdText">The text of the query.</param>
        /// <param name="rowCallback">callback that will extract results
        /// one row at a time.
        /// </param>
        public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback)
        {
            classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback);
        }

        /// <summary>
        /// Execute a query given IDbCommand's type and text and provided parameter
        /// information, reading a 
        /// single result set on a per-row basis with a <see cref="IRowCallback"/>.
        /// </summary>
        /// <param name="cmdType">The type of command</param>
        /// <param name="cmdText">The text of the query.</param>
        /// <param name="rowCallback">callback that will extract results
        /// one row at a time.
        /// </param>
        /// <param name="parameterName">The name of the parameter to map.</param>
        /// <param name="dbType">One of the database parameter type enumerations.</param>
        /// <param name="size">The length of the parameter. 0 if not applicable to parameter type.</param>
        /// <param name="parameterValue">The parameter value.</param>
        public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback,
                                         string parameterName, Enum dbType, int size, object parameterValue)
        {
            classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback,
                                                    parameterName, dbType, size, parameterValue);
        }

        /// <summary>
        /// Execute a query given IDbCommand's type and text and provided IDbParameters,
        /// reading a single result set on a per-row basis with a <see cref="IRowCallback"/>.
        /// </summary>
        /// <param name="cmdType">Type of the command.</param>
        /// <param name="cmdText">The text of the query.</param>
        /// <param name="rowCallback">callback that will extract results
        /// one row at a time.</param>
        /// <param name="parameter">The parameter collection to map.</param>
        public void QueryWithRowCallback(CommandType cmdType, string cmdText, IRowCallback rowCallback,
                                  IDbParameters parameter)
        {
            classicAdoTemplate.QueryWithRowCallback(cmdType, cmdText, rowCallback,
                                                    parameter);
        }

        #endregion

        #region Queries with RowCallback Delegate

        public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate)
        {
            classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate);

        }
        public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, ICommandSetter commandSetter)
        {
            throw new NotImplementedException();
        }

        public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate,
                        string parameterName, Enum dbType, int size, object parameterValue)
        {
            classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate,
                                                            parameterName, dbType, size, parameterValue);

        }

        public void QueryWithRowCallbackDelegate(CommandType cmdType, string sql, RowCallbackDelegate rowCallbackDelegate, IDbParameters parameters)
        {
            classicAdoTemplate.QueryWithRowCallbackDelegate(cmdType, sql, rowCallbackDelegate, parameters);

        }

        #endregion

        #region Queries with RowMapper<T>

        public IList<T> QueryWithRowMapper<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapper));

        }


        public IList<T> QueryWithRowMapper<T>(CommandType cmdType, String cmdText, IRowMapper<T> rowMapper,
                                              ICommandSetter commandSetter)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapper), commandSetter);

        }

        public IList<T> QueryWithRowMapper<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper,
                                              string parameterName, Enum dbType, int size, object parameterValue)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapper), parameterName, dbType, size, parameterValue);

        }

        public IList<T> QueryWithRowMapper<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper, IDbParameters parameters)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapper), parameters);

        }



        #endregion

        #region Queries with RowMapperDelegate<T>

        public IList<T> QueryWithRowMapperDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapperDelegate));

        }

        public IList<T> QueryWithRowMapperDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate,
                                                      ICommandSetter commandSetter)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapperDelegate),
                                               commandSetter);
        }

        public IList<T> QueryWithRowMapperDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate,
                                                      string parameterName, Enum dbType, int size, object parameterValue)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapperDelegate),
                                               parameterName, dbType, size, parameterValue);
        }

        public IList<T> QueryWithRowMapperDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate,
                                               IDbParameters parameters)
        {
            return QueryWithResultSetExtractor(cmdType, cmdText, new RowMapperResultSetExtractor<T>(rowMapperDelegate),
                                               parameters);
        }
        #endregion

        #region Queries with ResultSetExtractor<T>

        public T QueryWithResultSetExtractor<T>(CommandType cmdType,
                                                string cmdText,
                                                IResultSetExtractor<T> resultSetExtractor)
        {
            AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null");
            if (LOG.IsDebugEnabled)
            {
                LOG.Debug("Executing CommandText [" + cmdText + "]");
            }
            return Execute<T>(new QueryCallback<T>(this, cmdType, cmdText, resultSetExtractor, null));
        }

        public T QueryWithResultSetExtractor<T>(CommandType commandType, string cmdText, IResultSetExtractor<T> resultSetExtractor,
                                                string name, Enum dbType, int size, object parameterValue)
        {
            AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null");
            return Execute<T>(new QueryCallback<T>(this, commandType, cmdText, resultSetExtractor,
                                                   CreateDbParameters(name, dbType, size, parameterValue)));
        }

        public T QueryWithResultSetExtractor<T>(CommandType commandType, string cmdText,
                                                IResultSetExtractor<T> resultSetExtractor,
                                                IDbParameters parameters)
        {
            AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null");
            return Execute<T>(new QueryCallback<T>(this, commandType, cmdText, resultSetExtractor, parameters));
        }

        public T QueryWithResultSetExtractor<T>(CommandType cmdType, string cmdText,
                                                IResultSetExtractor<T> resultSetExtractor,
                                                ICommandSetter commandSetter)
        {
            AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null");
            return Execute<T>(new QueryCallbackWithCommandSetter<T>(this, cmdType, cmdText,
                                                                    resultSetExtractor,
                                                                    commandSetter));
        }

        public T QueryWithResultSetExtractor<T>(CommandType cmdType, string cmdText,
                                                IResultSetExtractor<T> resultSetExtractor,
                                                CommandSetterDelegate commandSetterDelegate)
        {
            AssertUtils.ArgumentNotNull(resultSetExtractor, "resultSetExtractor", "Result Set Extractor must not be null");
            return Execute<T>(new QueryCallbackWithCommandSetterDelegate<T>(this, cmdType, cmdText,
                                                                    resultSetExtractor,
                                                                    commandSetterDelegate));
        }

        #endregion


        #region Queries with ResultSetExtractorDelegate<T>

        public T QueryWithResultSetExtractorDelegate<T>(CommandType cmdType,
                                                        string cmdText,
                                                        ResultSetExtractorDelegate<T> resultSetExtractorDelegate)
        {
            AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null");
            AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null");
            if (LOG.IsDebugEnabled)
            {
                LOG.Debug("Executing CommandText [" + cmdText + "]");
            }

            return Execute<T>(new QueryCallback<T>(this, cmdType, cmdText, resultSetExtractorDelegate, null));
        }

        public T QueryWithResultSetExtractorDelegate<T>(CommandType commandType, string cmdText, ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                                        string paramenterName, Enum dbType, int size, object parameterValue)
        {
            AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null");
            AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null");
            return Execute<T>(new QueryCallback<T>(this, commandType, cmdText, resultSetExtractorDelegate,
                                                   CreateDbParameters(paramenterName, dbType, size, parameterValue)));
        }

        public T QueryWithResultSetExtractorDelegate<T>(CommandType commandType, string cmdText, ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                                IDbParameters parameters)
        {
            AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null");
            AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate  must not be null");
            return Execute<T>(new QueryCallback<T>(this, commandType, cmdText, resultSetExtractorDelegate, parameters));
        }

        public T QueryWithResultSetExtractorDelegate<T>(CommandType cmdType, string cmdText, ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                                        ICommandSetter commandSetter)
        {
            AssertUtils.ArgumentNotNull(cmdText, "cmdText", "CommandText must not be null");
            AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null");
            return Execute<T>(new QueryCallbackWithCommandSetter<T>(this, cmdType, cmdText,
                                                                    resultSetExtractorDelegate,
                                                                    commandSetter));
        }

        public T QueryWithResultSetExtractorDelegate<T>(CommandType cmdType, string cmdText, ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                                        CommandSetterDelegate commandSetterDelegate)
        {
            AssertUtils.ArgumentNotNull(resultSetExtractorDelegate, "resultSetExtractorDelegate", "Result set extractor delegate must not be null");
            return Execute<T>(new QueryCallbackWithCommandSetterDelegate<T>(this, cmdType, cmdText,
                                                                    resultSetExtractorDelegate,
                                                                    commandSetterDelegate));
        }

        #endregion

        #region Query for Object<T>

        public T QueryForObject<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper)
        {
            IList<T> results = QueryWithRowMapper(cmdType, cmdText, rowMapper);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }

        public T QueryForObject<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper, ICommandSetter commandSetter)
        {
            IList<T> results = QueryWithRowMapper(cmdType, cmdText, rowMapper, commandSetter);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }

        public T QueryForObject<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper, IDbParameters parameters)
        {
            IList<T> results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameters);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }


        public T QueryForObject<T>(CommandType cmdType, string cmdText, IRowMapper<T> rowMapper,
                                   string parameterName, Enum dbType, int size, object parameterValue)
        {
            IList<T> results = QueryWithRowMapper(cmdType, cmdText, rowMapper, parameterName, dbType, size, parameterValue);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }
        #endregion

        #region Query for ObjectDelegate<T>

        public T QueryForObjectDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate)
        {
            IList<T> results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }

        public T QueryForObjectDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate, ICommandSetter commandSetter)
        {
            IList<T> results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, commandSetter);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }

        public T QueryForObjectDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate, IDbParameters parameters)
        {
            IList<T> results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameters);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }


        public T QueryForObjectDelegate<T>(CommandType cmdType, string cmdText, RowMapperDelegate<T> rowMapperDelegate,
                                   string parameterName, Enum dbType, int size, object parameterValue)
        {
            IList<T> results = QueryWithRowMapperDelegate(cmdType, cmdText, rowMapperDelegate, parameterName, dbType, size, parameterValue);
            return DataAccessUtils.RequiredUniqueResultSet(results);
        }
        #endregion


        #region Query with CommandCreator

        public T QueryWithCommandCreator<T>(IDbCommandCreator cc, IResultSetExtractor<T> rse)
        {
            return QueryWithCommandCreator<T>(cc, rse, null);
        }

        public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback)
        {
            classicAdoTemplate.QueryWithCommandCreator(cc, rowCallback, null);
        }

        public IList<T> QueryWithCommandCreator<T>(IDbCommandCreator cc, IRowMapper<T> rowMapper)
        {
            return QueryWithCommandCreator<T>(cc, rowMapper, null);
        }

        public T QueryWithCommandCreator<T>(IDbCommandCreator cc, IResultSetExtractor<T> rse, IDictionary returnedParameters)
        {
            if (rse == null)
            {
                throw new ArgumentNullException("Result Set Extractor must not be null");
            }

            return Execute(cc, new AdoResultSetExtractorWithOutputParamsCommandCallback<T>(this, rse, returnedParameters));
        }


        public void QueryWithCommandCreator(IDbCommandCreator cc, IRowCallback rowCallback, IDictionary returnedParameters)
        {
            classicAdoTemplate.QueryWithCommandCreator(cc, rowCallback, returnedParameters);
        }


        public IList<T> QueryWithCommandCreator<T>(IDbCommandCreator cc, IRowMapper<T> rowMapper, IDictionary returnedParameters)
        {
            if (rowMapper == null)
            {
                throw new ArgumentNullException("rowMapper must not be null");
            }

            return Execute(cc, new AdoRowMapperQueryCommandCallback<T>(this, rowMapper, returnedParameters));
        }


        public IDictionary QueryWithCommandCreator<T>(IDbCommandCreator cc, IList namedResultSetProcessors)
        {
            return classicAdoTemplate.Execute(cc, new AdoResultProcessorsQueryCommandCallback<T>(this, namedResultSetProcessors)) as IDictionary;
        }
        

        public IDictionary QueryWithCommandCreator<T,U>(IDbCommandCreator cc, IList namedResultSetProcessors)
        {
            return classicAdoTemplate.Execute(cc, new AdoResultProcessorsQueryCommandCallback<T,U>(this, namedResultSetProcessors)) as IDictionary;
        }


        #endregion

        #region General Helper Methods

        /// <summary>
        /// Checks if DbProvider is not null and creates ExceptionTranslator if not LazyInit.
        /// </summary>
        public override void AfterPropertiesSet()
        {
            classicAdoTemplate.AfterPropertiesSet();
            classicAdoTemplate.CommandTimeout = CommandTimeout;
        }

        /// <summary>
        /// Creates the data reader wrapper for use in AdoTemplate callback methods.
        /// </summary>
        /// <param name="readerToWrap">The reader to wrap.</param>
        /// <returns>The data reader used in AdoTemplate callbacks</returns>
        public override IDataReader CreateDataReaderWrapper(IDataReader readerToWrap)
        {
            return classicAdoTemplate.CreateDataReaderWrapper(readerToWrap);
        }

        #endregion

        #region Parameter Creation Helper Methods

        /// <summary>
        /// Derives the parameters of a stored procedure including the return parameter
        /// </summary>
        /// <param name="procedureName">Name of the procedure.</param>
        /// <param name="includeReturnParameter">if set to <c>true</c> to include return parameter.</param>
        /// <returns>The stored procedure parameters</returns>
        public override IDataParameter[] DeriveParameters(string procedureName, bool includeReturnParameter)
        {
            return classicAdoTemplate.DeriveParameters(procedureName, includeReturnParameter);
        }

        #endregion





        #region Internal Helper classes

        internal class QueryCallback<T> : IDbCommandCallback<T>, ICommandTextProvider
        {
            private AdoTemplate adoTemplate;
            private IResultSetExtractor<T> rse;
            private ResultSetExtractorDelegate<T> resultSetExtractorDelegate;
            private CommandType commandType;
            private string commandText;
            private IDbParameters parameters;

            public QueryCallback(AdoTemplate adoTemplate,
                                 CommandType cmdType,
                                 string cmdText,
                                 IResultSetExtractor<T> rse,
                                 IDbParameters dbParameters)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.rse = rse;
                parameters = dbParameters;
            }

            public QueryCallback(AdoTemplate adoTemplate,
                         CommandType cmdType,
                         string cmdText,
                         ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                         IDbParameters dbParameters)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.resultSetExtractorDelegate = resultSetExtractorDelegate;
                parameters = dbParameters;
            }

            public string CommandText
            {
                get { return commandText; }
            }

            public T DoInCommand(IDbCommand command)
            {
                IDataReader reader = null;
                try
                {
                    command.CommandType = commandType;
                    command.CommandText = commandText;
                    ParameterUtils.CopyParameters(command, parameters);
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());
                    T returnValue = default(T);
                    if (rse != null)
                    {
                        returnValue = rse.ExtractData(reader);
                    }
                    else
                    {
                        returnValue = resultSetExtractorDelegate(reader);
                    }
                    return returnValue;
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                    ParameterUtils.CopyParameters(parameters, command);
                }
            }


        }

        internal class QueryCallbackWithCommandSetter<T> : IDbCommandCallback<T>, ICommandTextProvider
        {
            private AdoTemplate adoTemplate;
            private IResultSetExtractor<T> rse;
            private ResultSetExtractorDelegate<T> resultSetExtractorDelegate;
            private CommandType commandType;
            private string commandText;
            private ICommandSetter commandSetter;

            public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate,
                                                     CommandType cmdType,
                                                     string cmdText,
                                                     IResultSetExtractor<T> rse,
                                                     ICommandSetter commandSetter)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.rse = rse;
                this.commandSetter = commandSetter;
            }

            public QueryCallbackWithCommandSetter(AdoTemplate adoTemplate,
                                             CommandType cmdType,
                                             string cmdText,
                                             ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                             ICommandSetter commandSetter)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.resultSetExtractorDelegate = resultSetExtractorDelegate;
                this.commandSetter = commandSetter;
            }

            public string CommandText
            {
                get { return commandText; }
            }

            public T DoInCommand(IDbCommand command)
            {
                IDataReader reader = null;
                try
                {
                    command.CommandType = commandType;
                    command.CommandText = commandText;
                    if (commandSetter != null)
                    {
                        commandSetter.SetValues(command);
                    }
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());
                    if (rse != null)
                    {
                        return rse.ExtractData(reader);
                    }
                    else
                    {
                        return resultSetExtractorDelegate(reader);
                    }

                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
            }


        }

        internal class QueryCallbackWithCommandSetterDelegate<T> : IDbCommandCallback<T>, ICommandTextProvider
        {
            private AdoTemplate adoTemplate;
            private IResultSetExtractor<T> rse;
            private ResultSetExtractorDelegate<T> resultSetExtractorDelegate;
            private CommandType commandType;
            private string commandText;
            private CommandSetterDelegate commandSetterDelegate;

            public QueryCallbackWithCommandSetterDelegate(AdoTemplate adoTemplate,
                                                        CommandType cmdType,
                                                        string cmdText,
                                                        IResultSetExtractor<T> rse,
                                                        CommandSetterDelegate commandSetterDelegate)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.rse = rse;
                this.commandSetterDelegate = commandSetterDelegate;
            }

            public QueryCallbackWithCommandSetterDelegate(AdoTemplate adoTemplate,
                                                CommandType cmdType,
                                                string cmdText,
                                                ResultSetExtractorDelegate<T> resultSetExtractorDelegate,
                                                CommandSetterDelegate commandSetterDelegate)
            {
                this.adoTemplate = adoTemplate;
                commandType = cmdType;
                commandText = cmdText;
                this.resultSetExtractorDelegate = resultSetExtractorDelegate;
                this.commandSetterDelegate = commandSetterDelegate;
            }

            public string CommandText
            {
                get { return commandText; }
            }

            public T DoInCommand(IDbCommand command)
            {
                IDataReader reader = null;
                try
                {
                    command.CommandType = commandType;
                    command.CommandText = commandText;
                    if (commandSetterDelegate != null)
                    {
                        commandSetterDelegate(command);
                    }
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());
                    if (rse != null)
                    {
                        return rse.ExtractData(reader);
                    }
                    else
                    {
                        return resultSetExtractorDelegate(reader);
                    }
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
            }


        }

        internal class AdoRowMapperQueryCommandCallback<T> : IDbCommandCallback<IList<T>>
        {
            private AdoTemplate adoTemplate;
            private IRowMapper<T> rowMapper;
            private IDictionary returnedParameters;

            public AdoRowMapperQueryCommandCallback(AdoTemplate adoTemplate, IRowMapper<T> rowMapper, IDictionary returnedParameters)
            {
                this.adoTemplate = adoTemplate;
                this.rowMapper = rowMapper;
                this.returnedParameters = returnedParameters;
            }

            public IList<T> DoInCommand(IDbCommand command)
            {
                IList<T> objectList;
                //Extract the single returned result set
                IDataReader reader = null;
                try
                {
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());
                    RowMapperResultSetExtractor<T> rse = new RowMapperResultSetExtractor<T>(rowMapper, 1);
                    objectList = rse.ExtractData(reader);
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
                ParameterUtils.ExtractOutputParameters(returnedParameters, command);
                return objectList;
            }
        }

        internal class AdoResultSetExtractorWithOutputParamsCommandCallback<T> : IDbCommandCallback<T>
        {
            private AdoTemplate adoTemplate;
            private IResultSetExtractor<T> rse;
            private IDictionary returnedParameters;

            public AdoResultSetExtractorWithOutputParamsCommandCallback(AdoTemplate adoTemplate, IResultSetExtractor<T> rse, IDictionary returnedParameters)
            {
                this.adoTemplate = adoTemplate;
                this.rse = rse;
                this.returnedParameters = returnedParameters;
            }

            public T DoInCommand(IDbCommand command)
            {
                T returnVal;
                //Extract the single returned result set
                IDataReader reader = null;
                try
                {
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());
                    returnVal = rse.ExtractData(reader);
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
                ParameterUtils.ExtractOutputParameters(returnedParameters, command);
                return returnVal;
            }
        }

        internal class BaseAdoResultProcessorsQueryCommandCallback<T>
        {
            public static void ProcessNonGenericResultSetProcessor(NamedResultSetProcessor otherResultSetProcessor, IDataReader reader, IDictionary returnedResults)
            {
                string parameterName = otherResultSetProcessor.Name;
                if (otherResultSetProcessor.ResultSetProcessor is IResultSetExtractor)
                {
                    IResultSetExtractor rse = (IResultSetExtractor)otherResultSetProcessor.ResultSetProcessor;
                    object result = rse.ExtractData(reader);
                    returnedResults.Add(parameterName, result);
                }
                else if (otherResultSetProcessor.ResultSetProcessor is IRowMapper)
                {
                    IRowMapper rowMapper = (IRowMapper)otherResultSetProcessor.ResultSetProcessor;
                    object result = (new RowMapperResultSetExtractor(rowMapper)).ExtractData(reader);
                    returnedResults.Add(parameterName, result);
                }
                else if (otherResultSetProcessor.ResultSetProcessor is IRowCallback)
                {
                    IRowCallback rowCallback = (IRowCallback)otherResultSetProcessor.ResultSetProcessor;
                    (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader);
                    returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback");
                }
            }

            public static void ProcessFirstGenericResultSetProcessor(NamedResultSetProcessor<T> firstResultSetProcessor, IDataReader reader, IDictionary returnedResults)
            {
                string parameterName = firstResultSetProcessor.Name;
                if (firstResultSetProcessor.ResultSetExtractor != null)
                {
                    IResultSetExtractor<T> rse = firstResultSetProcessor.ResultSetExtractor;
                    T result = rse.ExtractData(reader);
                    returnedResults.Add(parameterName, result);
                }
                else if (firstResultSetProcessor.RowMapper != null)
                {
                    IRowMapper<T> rowMapper = firstResultSetProcessor.RowMapper;
                    object result = (new RowMapperResultSetExtractor<T>(rowMapper)).ExtractData(reader);
                    returnedResults.Add(parameterName, result);
                }
                else if (firstResultSetProcessor.RowCallback != null)
                {
                    IRowCallback rowCallback = firstResultSetProcessor.RowCallback;
                    (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader);
                    returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback");
                }
            }
        }

        internal class AdoResultProcessorsQueryCommandCallback<T> : BaseAdoResultProcessorsQueryCommandCallback<T>, ICommandCallback
        {
            private AdoTemplate adoTemplate;
            private IList namedResultSetProcessors;

            public AdoResultProcessorsQueryCommandCallback(AdoTemplate adoTemplate, IList namedResultSetProcessors)
            {
                this.adoTemplate = adoTemplate;
                this.namedResultSetProcessors = namedResultSetProcessors;
            }

            public object DoInCommand(IDbCommand command)
            {
                IDictionary returnedResults = new Hashtable();
                int resultSetIndex = 0;
                IDataReader reader = null;
                try
                {
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());

                    //TODO On >= .NET 2.0 platforms make use of DbDataReader.HasRows property to
                    //     see if there is a result set.  now currently assuming matching
                    //     NamedResultSetProcessor/ResultSet pairs.
             
                    do
                    {
                        if (namedResultSetProcessors.Count == 0)
                        {
                            //We could just have output parameters and/or return value, that is, no result sets
                            //If we didn't register a result set processor, it is likely that a result set wasn't expected.
                            break;
                        }
                        NamedResultSetProcessor<T> firstResultSetProcessor = null;
                        NamedResultSetProcessor otherResultSetProcessor = null;
                        try
                        {
                            if (resultSetIndex == 0)
                            {
                                firstResultSetProcessor
                                    = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor<T>;
                                //Will have possibility of run-time type error if using QueryWithCommandCreator
                                if (firstResultSetProcessor == null)
                                {
                                    LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex +
                                              ", is not of expected type NamedResultSetProcessor<T>.  Type = " +
                                              namedResultSetProcessors[resultSetIndex].GetType() +
                                              "; Skipping processing for this result set.");
                                    continue;
                                }
                            }
                            else
                            {
                                otherResultSetProcessor =
                                    namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor;
                                if (otherResultSetProcessor == null)
                                {

                                    LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex +
                                              ", is not of expected type NamedResultSetProcessor  Type = " +
                                              namedResultSetProcessors[resultSetIndex].GetType() +
                                              "; Skipping processing for this result set.");
                                    continue;
                                }
                            }
                        }
                        catch (IndexOutOfRangeException e)
                        {
                            LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e);
                            continue;
                        }
                        catch (ArgumentOutOfRangeException e)
                        {
                            LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e);
                            continue;
                        }


                        if (resultSetIndex == 0)
                        {
                            ProcessFirstGenericResultSetProcessor(firstResultSetProcessor, reader, returnedResults);
                        }
                        else
                        {
                            ProcessNonGenericResultSetProcessor(otherResultSetProcessor, reader, returnedResults);
                        }
                        resultSetIndex++;

                    } while (reader.NextResult());
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
                ParameterUtils.ExtractOutputParameters(returnedResults, command);
                return returnedResults;
            }
        }


        internal class AdoResultProcessorsQueryCommandCallback<T, U> : BaseAdoResultProcessorsQueryCommandCallback<T>, ICommandCallback
        {
            private AdoTemplate adoTemplate;
            private IList namedResultSetProcessors;

            public AdoResultProcessorsQueryCommandCallback(AdoTemplate adoTemplate, IList namedResultSetProcessors)
            {
                this.adoTemplate = adoTemplate;
                this.namedResultSetProcessors = namedResultSetProcessors;
            }

            public object DoInCommand(IDbCommand command)
            {
                IDictionary returnedResults = new Hashtable();
                int resultSetIndex = 0;
                IDataReader reader = null;
                try
                {
                    reader = adoTemplate.CreateDataReaderWrapper(command.ExecuteReader());

                    //TODO On >= .NET 2.0 platforms make use of DbDataReader.HasRows property to
                    //     see if there is a result set.  now currently assuming matching
                    //     NamedResultSetProcessor/ResultSet pairs.

                    do
                    {
                        if (namedResultSetProcessors.Count == 0)
                        {
                            //We could just have output parameters and/or return value, that is, no result sets
                            //If we didn't register a result set processor, it is likely that a result set wasn't expected.
                            break;
                        }
                        NamedResultSetProcessor<T> firstResultSetProcessor = null;
                        NamedResultSetProcessor<U> secondResultSetProcessor = null;
                        NamedResultSetProcessor otherResultSetProcessor = null;
                        try
                        {
                            if (resultSetIndex == 0)
                            {
                                firstResultSetProcessor = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor<T>;
                                //Will only have possibility of run-time type error if using QueryWithCommandCreator
                                if (firstResultSetProcessor == null)
                                {

                                    LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex +
                                              ", is not of expected type NamedResultSetProcessor<T>  Type = " +
                                              namedResultSetProcessors[resultSetIndex].GetType() +
                                              "; Skipping processing for this result set.");
                                    continue;                                    
                                }
                            } else if (resultSetIndex == 1)
                            {
                                secondResultSetProcessor
                                    = namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor<U>;
                                if (secondResultSetProcessor == null)
                                {

                                    LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex +
                                              ", is not of expected type NamedResultSetProcessor<T>  Type = " +
                                              namedResultSetProcessors[resultSetIndex].GetType() +
                                              "; Skipping processing for this result set.");
                                    continue;
                                }
                            } else
                            {
                                otherResultSetProcessor =
                                    namedResultSetProcessors[resultSetIndex] as NamedResultSetProcessor;
                                if (otherResultSetProcessor == null)
                                {

                                    LOG.Error("NamedResultSetProcessor for result set index " + resultSetIndex +
                                              ", is not of expected type NamedResultSetProcessor  Type = " +
                                              namedResultSetProcessors[resultSetIndex].GetType() +
                                              "; Skipping processing for this result set.");
                                    continue;
                                }
                            }

                        }
                        catch (IndexOutOfRangeException e)
                        {
                            LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e);
                            continue;
                        }
                        catch (ArgumentOutOfRangeException e)
                        {
                            LOG.Error("No NamedResultSetProcessor associated with result set index " + resultSetIndex, e);
                            continue;
                        }

                        if (resultSetIndex == 0)
                        {
                            ProcessFirstGenericResultSetProcessor(firstResultSetProcessor, reader, returnedResults);
                        }
                        else if (resultSetIndex == 1)
                        {
                            string parameterName = secondResultSetProcessor.Name;
                            if (secondResultSetProcessor.ResultSetExtractor != null)
                            {
                                IResultSetExtractor<U> rse = secondResultSetProcessor.ResultSetExtractor;
                                U result = rse.ExtractData(reader);
                                returnedResults.Add(parameterName, result);
                            }
                            else if (secondResultSetProcessor.RowMapper != null)
                            {
                                IRowMapper<U> rowMapper = secondResultSetProcessor.RowMapper;
                                object result = (new RowMapperResultSetExtractor<U>(rowMapper)).ExtractData(reader);
                                returnedResults.Add(parameterName, result);
                            }
                            else if (secondResultSetProcessor.RowCallback != null)
                            {
                                //In this case a dummy type parameter would need to be specified a better alternative would
                                //be to to use the single type parameterization of this callback.
                                IRowCallback rowCallback = secondResultSetProcessor.RowCallback;
                                (new RowCallbackResultSetExtractor(rowCallback)).ExtractData(reader);
                                returnedResults.Add(parameterName, "ResultSet returned was processed by an IRowCallback");
                            }
                        }
                        else
                        {
                            ProcessNonGenericResultSetProcessor(otherResultSetProcessor, reader, returnedResults);
                        }
                        resultSetIndex++;

                    } while (reader.NextResult());
                }
                finally
                {
                    AdoUtils.CloseReader(reader);
                }
                ParameterUtils.ExtractOutputParameters(returnedResults, command);
                return returnedResults;
            }


        }
        #endregion

    }
}
#endif