Mercurial > pub > bltoolkit
diff Source/Data/DbManager.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/Data/DbManager.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,4485 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Data.Common; +using System.Data.SqlTypes; +using System.Diagnostics; +using System.Text; + +#region ReSharper disable +// ReSharper disable UnusedParameter.Local +#pragma warning disable 1589 +#endregion + +namespace BLToolkit.Data +{ + using Common; + using DataProvider; + using Mapping; + using Properties; + using Reflection; + using Sql; + + /// <summary> + /// The <b>DbManager</b> is a primary class of the <see cref="BLToolkit.Data"/> namespace + /// that can be used to execute commands of different database providers. + /// </summary> + /// <remarks> + /// When the <b>DbManager</b> goes out of scope, it does not close the internal connection object. + /// Therefore, you must explicitly close the connection by calling <see cref="Close"/> or + /// <see cref="Dispose(bool)"/>. Also, you can use the C# <b>using</b> statement. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="DbManager"]/*' /> + [DesignerCategory(@"Code")] + public partial class DbManager: Component + { + #region Init + + public DbManager(DataProviderBase dataProvider, string connectionString) + { + if (dataProvider == null) throw new ArgumentNullException("dataProvider"); + if (connectionString == null) throw new ArgumentNullException("connectionString"); + + _dataProvider = dataProvider; + _connection = dataProvider.CreateConnectionObject(); + + _connection.ConnectionString = connectionString; + + _dataProvider.InitDbManager(this); + } + + public DbManager(DataProviderBase dataProvider, IDbConnection connection) + { + if (dataProvider == null) throw new ArgumentNullException("dataProvider"); + if (connection == null) throw new ArgumentNullException("connection"); + + _dataProvider = dataProvider; + _connection = connection; + + _dataProvider.InitDbManager(this); + } + + public DbManager(DataProviderBase dataProvider, IDbTransaction transaction) + { + if (dataProvider == null) throw new ArgumentNullException("dataProvider"); + if (transaction == null) throw new ArgumentNullException("transaction"); + + _dataProvider = dataProvider; + _connection = transaction.Connection; + _transaction = transaction; + _closeTransaction = false; + + _dataProvider.InitDbManager(this); + } + + DbManager(int n) + { + } + + public virtual DbManager Clone() + { + var clone = + new DbManager(0) + { + _configurationString = _configurationString, + _dataProvider = _dataProvider, + _mappingSchema = _mappingSchema + }; + + if (_connection != null) + clone._connection = CloneConnection(); + + return clone; + } + + public string LastQuery; + + #endregion + + #region Public Properties + + private MappingSchema _mappingSchema = Map.DefaultSchema; + /// <summary> + /// Gets the <see cref="BLToolkit.Mapping.MappingSchema"/> + /// used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A mapping schema. + /// </value> + public MappingSchema MappingSchema + { + [DebuggerStepThrough] + get { return _mappingSchema; } + set { _mappingSchema = value ?? Map.DefaultSchema; } + } + + private DataProviderBase _dataProvider; + /// <summary> + /// Gets the <see cref="BLToolkit.Data.DataProvider.DataProviderBase"/> + /// used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A data provider. + /// </value> + /// <include file="Examples.xml" path='examples/db[@name="DataProvider"]/*' /> + public DataProviderBase DataProvider + { + [DebuggerStepThrough] + get { return _dataProvider; } + protected set { _dataProvider = value; } + } + + private static TraceSwitch _traceSwitch; + public static TraceSwitch TraceSwitch + { + get { return _traceSwitch ?? (_traceSwitch = new TraceSwitch("DbManager", "DbManager trace switch", +#if DEBUG + "Warning" +#else + "Off" +#endif + )); } + set { _traceSwitch = value; } + } + + public static void TurnTraceSwitchOn() + { + TraceSwitch = new TraceSwitch("DbManager", "DbManager trace switch", "Info"); + } + + public static Action<string,string> WriteTraceLine = (message, displayName) => Debug.WriteLine(message, displayName); + + private bool _canRaiseEvents = true; + public new bool CanRaiseEvents + { + get { return _canRaiseEvents && base.CanRaiseEvents; } + set { _canRaiseEvents = value; } + } + + /// <summary> + /// Use plain text query instead of using command parameters + /// </summary> + public bool UseQueryText { get; set; } + + #endregion + + #region Connection + + private bool _closeConnection; + private IDbConnection _connection; + /// <summary> + /// Gets or sets the <see cref="IDbConnection"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// The connection to the data source. + /// </value> + /// <remarks> + /// Then you set a connection object, it has to match the data source type. + /// </remarks> + /// <exception cref="DataException"> + /// A connection does not match the data source type. + /// </exception> + /// <include file="Examples.xml" path='examples/db[@name="Connection"]/*' /> + public IDbConnection Connection + { + [DebuggerStepThrough] + get + { + if (_connection.State == ConnectionState.Closed) + OpenConnection(); + return _connection; + } + + set + { + if (value == null) + throw new ArgumentNullException("value"); + + if (value.GetType() != _dataProvider.ConnectionType) + InitDataProvider(value); + + _connection = value; + _closeConnection = false; + } + } + + [Obsolete] + protected virtual string GetConnectionString(IDbConnection connection) + { + return connection.ConnectionString; + } + + private void OpenConnection() + { + ExecuteOperation(OperationType.OpenConnection, _connection.Open); + _closeConnection = true; + } + + /// <summary> + /// Closes the connection to the database. + /// </summary> + /// <remarks> + /// The <b>Close</b> method rolls back any pending transactions + /// and then closes the connection. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Close()"]/*' /> + /// <seealso cref="Dispose(bool)"/> + public void Close() + { + if (OnClosing != null) + OnClosing(this, EventArgs.Empty); + + if (_selectCommand != null) { _selectCommand.Dispose(); _selectCommand = null; } + if (_insertCommand != null) { _insertCommand.Dispose(); _insertCommand = null; } + if (_updateCommand != null) { _updateCommand.Dispose(); _updateCommand = null; } + if (_deleteCommand != null) { _deleteCommand.Dispose(); _deleteCommand = null; } + + if (_transaction != null && _closeTransaction) + { + ExecuteOperation(OperationType.DisposeTransaction, _transaction.Dispose); + _transaction = null; + } + + if (_connection != null && _closeConnection) + { + ExecuteOperation(OperationType.CloseConnection, _connection.Dispose); + _connection = null; + } + + if (OnClosed != null) + OnClosed(this, EventArgs.Empty); + } + + private bool? _isMarsEnabled; + public bool IsMarsEnabled + { + get + { + if (_isMarsEnabled == null) + _isMarsEnabled = DataProvider.IsMarsEnabled(Connection); + + return _isMarsEnabled.Value; + } + set { _isMarsEnabled = value; } + } + + #endregion + + #region Transactions + + private bool _closeTransaction = true; + private IDbTransaction _transaction; + /// <summary> + /// Gets the <see cref="IDbTransaction"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// The <see cref="IDbTransaction"/>. The default value is a null reference. + /// </value> + /// <remarks> + /// You have to call the <see cref="BeginTransaction()"/> method to begin a transaction. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Transaction"]/*' /> + /// <seealso cref="BeginTransaction()"/> + public IDbTransaction Transaction + { + [DebuggerStepThrough] + get { return _transaction; } + } + + /// <summary> + /// Begins a database transaction. + /// </summary> + /// <remarks> + /// Once the transaction has completed, you must explicitly commit or roll back the transaction + /// by using the <see cref="System.Data.IDbTransaction.Commit"/>> or + /// <see cref="System.Data.IDbTransaction.Rollback"/> methods. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="BeginTransaction()"]/*' /> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + /// <seealso cref="Transaction"/> + public virtual DbManager BeginTransaction() + { + return BeginTransaction(IsolationLevel.ReadCommitted); + } + + /// <summary> + /// Begins a database transaction with the specified <see cref="IsolationLevel"/> value. + /// </summary> + /// <remarks> + /// Once the transaction has completed, you must explicitly commit or roll back the transaction + /// by using the <see cref="System.Data.IDbTransaction.Commit"/> or + /// <see cref="System.Data.IDbTransaction.Rollback"/> methods. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="BeginTransaction(IsolationLevel)"]/*' /> + /// <param name="il">One of the <see cref="IsolationLevel"/> values.</param> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + public virtual DbManager BeginTransaction(IsolationLevel il) + { + // If transaction is open, we dispose it, it will rollback all changes. + // + if (_transaction != null) + { + ExecuteOperation(OperationType.DisposeTransaction, _transaction.Dispose); + } + + // Create new transaction object. + // + _transaction = ExecuteOperation( + OperationType.BeginTransaction, + () => Connection.BeginTransaction(il)); + + _closeTransaction = true; + + // If the active command exists. + // + if (_selectCommand != null) _selectCommand.Transaction = _transaction; + if (_insertCommand != null) _insertCommand.Transaction = _transaction; + if (_updateCommand != null) _updateCommand.Transaction = _transaction; + if (_deleteCommand != null) _deleteCommand.Transaction = _transaction; + + return this; + } + + /// <summary> + /// Commits the database transaction. + /// </summary> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + public virtual DbManager CommitTransaction() + { + if (_transaction != null) + { + ExecuteOperation(OperationType.CommitTransaction, _transaction.Commit); + + if (_closeTransaction) + { + ExecuteOperation(OperationType.DisposeTransaction, _transaction.Dispose); + _transaction = null; + } + } + + return this; + } + + /// <summary> + /// Rolls back a transaction from a pending state. + /// </summary> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + public virtual DbManager RollbackTransaction() + { + if (_transaction != null) + { + ExecuteOperation(OperationType.RollbackTransaction, _transaction.Rollback); + + if (_closeTransaction) + { + ExecuteOperation(OperationType.DisposeTransaction, _transaction.Dispose); + _transaction = null; + } + } + + return this; + } + + #endregion + + #region Commands + + private IDbCommand _selectCommand; + /// <summary> + /// Gets the <see cref="IDbCommand"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A <see cref="IDbCommand"/> used during executing query. + /// </value> + /// <remarks> + /// The <b>Command</b> can be used to access command parameters. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Command"]/*' /> + public IDbCommand Command + { + [DebuggerStepThrough] + get { return SelectCommand; } + } + + /// <summary> + /// Gets the select <see cref="IDbCommand"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A <see cref="IDbCommand"/> used during executing query. + /// </value> + /// <remarks> + /// The <b>SelectCommand</b> can be used to access select command parameters. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Command"]/*' /> + public IDbCommand SelectCommand + { + [DebuggerStepThrough] + get { return _selectCommand = OnInitCommand(_selectCommand); } + } + + private IDbCommand _insertCommand; + /// <summary> + /// Gets the insert <see cref="IDbCommand"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A <see cref="IDbCommand"/> used during executing query. + /// </value> + /// <remarks> + /// The <b>InsertCommand</b> can be used to access insert command parameters. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Command"]/*' /> + public IDbCommand InsertCommand + { + [DebuggerStepThrough] + get { return _insertCommand = OnInitCommand(_insertCommand); } + } + + private IDbCommand _updateCommand; + /// <summary> + /// Gets the update <see cref="IDbCommand"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A <see cref="IDbCommand"/> used during executing query. + /// </value> + /// <remarks> + /// The <b>UpdateCommand</b> can be used to access update command parameters. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Command"]/*' /> + public IDbCommand UpdateCommand + { + [DebuggerStepThrough] + get { return _updateCommand = OnInitCommand(_updateCommand); } + } + + private IDbCommand _deleteCommand; + /// <summary> + /// Gets the delete <see cref="IDbCommand"/> used by this instance of the <see cref="DbManager"/>. + /// </summary> + /// <value> + /// A <see cref="IDbCommand"/> used during executing query. + /// </value> + /// <remarks> + /// The <b>DeleteCommand</b> can be used to access delete command parameters. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="Command"]/*' /> + public IDbCommand DeleteCommand + { + [DebuggerStepThrough] + get { return _deleteCommand = OnInitCommand(_deleteCommand); } + } + + /// <summary> + /// Initializes a command and raises the <see cref="InitCommand"/> event. + /// </summary> + protected virtual IDbCommand OnInitCommand(IDbCommand command) + { + if (command == null) + { + // Create a command object. + // + command = _dataProvider.CreateCommandObject(Connection); + + // If an active transaction exists. + // + if (Transaction != null) + { + command.Transaction = Transaction; + } + } + + if (CanRaiseEvents) + { + var handler = (InitCommandEventHandler)Events[_eventInitCommand]; + + if (handler != null) + handler(this, new InitCommandEventArgs(command)); + } + + return command; + } + + /// <summary> + /// Helper function. Creates the command object and sets command type and command text. + /// </summary> + /// <param name="commandAction">Command action.</param> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> + /// (stored procedure, text, etc.)</param> + /// <param name="sql">The SQL statement.</param> + /// <returns>The command object.</returns> + private IDbCommand GetCommand(CommandAction commandAction, CommandType commandType, string sql) + { + var command = GetCommand(commandAction, commandType); + + command.Parameters.Clear(); + command.CommandType = commandType; + command.CommandText = sql; + + return command; + } + + #endregion + + #region Events + + public event EventHandler OnClosing; + public event EventHandler OnClosed; + + private static readonly object _eventBeforeOperation = new object(); + /// <summary> + /// Occurs when a server-side operation is about to start. + /// </summary> + public event OperationTypeEventHandler BeforeOperation + { + add { Events.AddHandler (_eventBeforeOperation, value); } + remove { Events.RemoveHandler(_eventBeforeOperation, value); } + } + + private static readonly object _eventAfterOperation = new object(); + /// <summary> + /// Occurs when a server-side operation is complete. + /// </summary> + public event OperationTypeEventHandler AfterOperation + { + add { Events.AddHandler (_eventAfterOperation, value); } + remove { Events.RemoveHandler(_eventAfterOperation, value); } + } + + private static readonly object _eventOperationException = new object(); + /// <summary> + /// Occurs when a server-side operation is failed to execute. + /// </summary> + public event OperationExceptionEventHandler OperationException + { + add { Events.AddHandler (_eventOperationException, value); } + remove { Events.RemoveHandler(_eventOperationException, value); } + } + + private static readonly object _eventInitCommand = new object(); + /// <summary> + /// Occurs when the <see cref="Command"/> is initializing. + /// </summary> + public event InitCommandEventHandler InitCommand + { + add { Events.AddHandler (_eventInitCommand, value); } + remove { Events.RemoveHandler(_eventInitCommand, value); } + } + + /// <summary> + /// Raises the <see cref="BeforeOperation"/> event. + /// </summary> + /// <param name="op">The <see cref="OperationType"/>.</param> + protected virtual void OnBeforeOperation(OperationType op) + { + if (CanRaiseEvents) + { + var handler = (OperationTypeEventHandler)Events[_eventBeforeOperation]; + if (handler != null) + handler(this, new OperationTypeEventArgs(op)); + } + } + + /// <summary> + /// Raises the <see cref="AfterOperation"/> event. + /// </summary> + /// <param name="op">The <see cref="OperationType"/>.</param> + protected virtual void OnAfterOperation(OperationType op) + { + if (CanRaiseEvents) + { + var handler = (OperationTypeEventHandler)Events[_eventAfterOperation]; + if (handler != null) + handler(this, new OperationTypeEventArgs(op)); + } + } + + /// <summary> + /// Raises the <see cref="OperationException"/> event. + /// </summary> + /// <param name="op">The <see cref="OperationType"/>.</param> + /// <param name="ex">The <see cref="Exception"/> occurred.</param> + protected virtual void OnOperationException(OperationType op, DataException ex) + { + if (CanRaiseEvents) + { + var handler = (OperationExceptionEventHandler)Events[_eventOperationException]; + if (handler != null) + handler(this, new OperationExceptionEventArgs(op, ex)); + } + + throw ex; + } + + #endregion + + #region Protected Methods + + private IDataReader ExecuteReaderInternal() + { + return ExecuteReader(CommandBehavior.Default); + } + + private IDataReader ExecuteReaderInternal(CommandBehavior commandBehavior) + { + return ExecuteOperation( + OperationType.ExecuteReader, + () => + _dataProvider.GetDataReader(_mappingSchema, SelectCommand.ExecuteReader(commandBehavior))); + } + + private int ExecuteNonQueryInternal() + { + return ExecuteOperation<int>(OperationType.ExecuteNonQuery, SelectCommand.ExecuteNonQuery); + } + + #endregion + + #region Parameters + + private IDbDataParameter[] CreateSpParameters(string spName, object[] parameterValues, bool openNewConnectionToDiscoverParameters) + { + // Pull the parameters for this stored procedure from + // the parameter cache (or discover them & populate the cache) + // + var spParameters = GetSpParameters(spName, true, openNewConnectionToDiscoverParameters); + + // DbParameters are bound by name, plain parameters by order + // + var dbParameters = false; + + if (parameterValues == null || parameterValues.Length == 0 || + parameterValues[0] is IDbDataParameter || parameterValues[0] is IDbDataParameter[]) + { + // The PrepareParameters method may add some additional parameters. + // + parameterValues = PrepareParameters(parameterValues); + + if (parameterValues == null || parameterValues.Length == 0) + return spParameters; + + dbParameters = true; + } + + if (spParameters == null/* || commandParameters.Length == 0*/) + { + spParameters = new IDbDataParameter[parameterValues.Length]; + + if (dbParameters) + parameterValues.CopyTo(spParameters, 0); + else + for (var i = 0; i < parameterValues.Length; i++) + spParameters[i] = Parameter("?", parameterValues[i]); + + return spParameters; + } + + if (dbParameters) + { + // If we receive an array of IDbDataParameter, + // we need to copy parameters to the IDbDataParameter[]. + // + foreach (var spParam in spParameters) + { + var spParamName = spParam.ParameterName; + var found = false; + + foreach (IDbDataParameter paramWithValue in parameterValues) + { + var parameterNamesEqual = _dataProvider.ParameterNamesEqual(spParamName, paramWithValue.ParameterName); + if (!parameterNamesEqual) + { + var convertedParameterName = + _dataProvider.Convert(paramWithValue.ParameterName, ConvertType.NameToSprocParameter).ToString(); + + parameterNamesEqual = _dataProvider.ParameterNamesEqual(spParamName, convertedParameterName); + } + + if (!parameterNamesEqual) continue; + + if (spParam.Direction != paramWithValue.Direction) + { + if (TraceSwitch.TraceWarning) + WriteTraceLine( + string.Format( + "Stored Procedure '{0}'. Parameter '{1}' has different direction '{2}'. Should be '{3}'.", + spName, spParamName, spParam.Direction, paramWithValue.Direction), + TraceSwitch.DisplayName); + + spParam.Direction = paramWithValue.Direction; + } + + if (spParam.Direction != ParameterDirection.Output) + spParam.Value = paramWithValue.Value; + + paramWithValue.ParameterName = spParamName; + found = true; + break; + } + + if (found == false && ( + spParam.Direction == ParameterDirection.Input || + spParam.Direction == ParameterDirection.InputOutput)) + { + if (TraceSwitch.TraceWarning) + WriteTraceLine( + string.Format("Stored Procedure '{0}'. Parameter '{1}' not assigned.", spName, spParamName), + TraceSwitch.DisplayName); + + spParam.SourceColumn = _dataProvider.Convert(spParamName, ConvertType.SprocParameterToName).ToString(); + } + } + } + else + { + // Assign the provided values to the parameters based on parameter order. + // + AssignParameterValues(spName, spParameters, parameterValues); + } + + return spParameters; + } + + ///<summary> + /// Creates an one-dimension array of <see cref="IDbDataParameter"/> + /// from any combination on IDbDataParameter, IDbDataParameter[] or null references. + /// Null references are stripped, arrays and single parameters are combined + /// into a new array. + ///</summary> + /// <remarks>When two or more parameters has the same name, + /// the first parameter is used, all the rest are ignored.</remarks> + ///<param name="parameters">Array of IDbDataParameter, IDbDataParameter[] or null references.</param> + ///<returns>An normalized array of <see cref="IDbDataParameter"/> without null references.</returns> + ///<exception cref="ArgumentException">The parameter <paramref name="parameters"/> + /// contains anything except IDbDataParameter, IDbDataParameter[] or null reference.</exception> + public virtual IDbDataParameter[] PrepareParameters(object[] parameters) + { + if (parameters == null || parameters.Length == 0) + return null; + + // Little optimization. + // Check if we have only one single ref parameter. + // + object refParam = null; + + foreach (var p in parameters) + if (p != null) + { + if (refParam != null) + { + refParam = null; + break; + } + + refParam = p; + } + + if (refParam is IDbDataParameter[]) + { + return (IDbDataParameter[])refParam; + } + + if (refParam is IDbDataParameter) + { + var oneParameterArray = new IDbDataParameter[1]; + oneParameterArray[0] = (IDbDataParameter)refParam; + return oneParameterArray; + } + + var list = new List<IDbDataParameter>(parameters.Length); + var hash = new Dictionary<string, IDbDataParameter>(parameters.Length); + + foreach (var o in parameters) + if (o is IDbDataParameter) + { + var p = (IDbDataParameter) o; + + if (!hash.ContainsKey(p.ParameterName)) + { + list.Add(p); + hash.Add(p.ParameterName, p); + } + } + else if (o is IDbDataParameter[]) + { + foreach (var p in (IDbDataParameter[]) o) + if (!hash.ContainsKey(p.ParameterName)) + { + list.Add(p); + hash.Add(p.ParameterName, p); + } + } + else if (o != null && o != DBNull.Value) + throw new ArgumentException( + Resources.DbManager_NotDbDataParameter, "parameters"); + + return list.ToArray(); + } + + /// <summary> + /// This method is used to attach array of <see cref="IDbDataParameter"/> to a <see cref="IDbCommand"/>. + /// </summary> + /// <param name="command">The command to which the parameters will be added</param> + /// <param name="commandParameters">An array of IDbDataParameters tho be added to command</param> + private void AttachParameters(IDbCommand command, IEnumerable<IDbDataParameter> commandParameters) + { + command.Parameters.Clear(); + + foreach (var p in commandParameters) + _dataProvider.AttachParameter(command, p); + } + + private static readonly Dictionary<string, IDbDataParameter[]> _paramCache = + new Dictionary<string, IDbDataParameter[]>(); + private static readonly object _paramCacheLock = new object(); + + /// <summary> + /// Resolve at run time the appropriate set of parameters for a stored procedure. + /// </summary> + /// <param name="spName">The name of the stored procedure.</param> + /// <param name="includeReturnValueParameter">Whether or not to include their return value parameter.</param> + /// <param name="openNewConnection"></param> + /// <returns></returns> + protected virtual IDbDataParameter[] DiscoverSpParameters(string spName, bool includeReturnValueParameter, bool openNewConnection) + { + var con = openNewConnection ? CloneConnection() : _connection; + + try + { + if (con.State == ConnectionState.Closed) + { + ExecuteOperation(OperationType.OpenConnection, con.Open); + if (!openNewConnection) + _closeConnection = true; + } + + using (var cmd = con.CreateCommand()) + { + cmd.CommandType = CommandType.StoredProcedure; + cmd.CommandText = spName; + + var res = ExecuteOperation(OperationType.DeriveParameters, () => _dataProvider.DeriveParameters(cmd)); + + if (openNewConnection) + ExecuteOperation(OperationType.CloseConnection, con.Close); + + if (res == false) + return null; + + if (includeReturnValueParameter == false) + { + // All known data providers always treat + // the return value as first parameter. + // + cmd.Parameters.RemoveAt(0); + } + + var discoveredParameters = new IDbDataParameter[cmd.Parameters.Count]; + + for (var i = 0; i < cmd.Parameters.Count; i++) + discoveredParameters[i] = (IDbDataParameter)cmd.Parameters[i]; + + return discoveredParameters; + } + } + finally + { + if (con != null && openNewConnection) + con.Dispose(); + } + } + + /// <summary> + /// Copies cached parameter array. + /// </summary> + /// <param name="originalParameters">The original parameter array.</param> + /// <returns>The result array.</returns> + private IDbDataParameter[] CloneParameters(IDbDataParameter[] originalParameters) + { + if (originalParameters == null) + return null; + + var clonedParameters = new IDbDataParameter[originalParameters.Length]; + + for (var i = 0; i < originalParameters.Length; i++) + clonedParameters[i] = _dataProvider.CloneParameter(originalParameters[i]); + + return clonedParameters; + } + + /// <summary> + /// Retrieves the set of parameters appropriate for the stored procedure. + /// </summary> + /// <remarks> + /// This method will query the database for this information, + /// and then store it in a cache for future requests. + /// </remarks> + /// <param name="spName">The name of the stored procedure.</param> + /// <param name="includeReturnValueParameter">A boolean value indicating + /// whether the return value parameter should be included in the results.</param> + /// <param name="openNewConnectionToDiscoverParameters"></param> + /// <returns>An array of the <see cref="IDbDataParameter"/>.</returns> + public IDbDataParameter[] GetSpParameters(string spName, bool includeReturnValueParameter, bool openNewConnectionToDiscoverParameters) + { + var key = string.Format("{0}:{1}:{2}", GetConnectionHash(), spName, includeReturnValueParameter); + + IDbDataParameter[] cachedParameters; + + // It is thread safe enought to check for a key and get its value without a lock. + // + if (!_paramCache.TryGetValue(key, out cachedParameters)) + { + lock (_paramCacheLock) + { + // There is a possible race condition since the operation may take a time. + // + if (!_paramCache.TryGetValue(key, out cachedParameters)) + { + cachedParameters = DiscoverSpParameters(spName, includeReturnValueParameter, openNewConnectionToDiscoverParameters); + _paramCache.Add(key, cachedParameters); + } + } + } + + return CloneParameters(cachedParameters); + } + + /// <summary> + /// This method assigns an array of values to an array of parameters. + /// </summary> + /// <param name="spName"></param> + /// <param name="commandParameters">array of IDbDataParameters to be assigned values</param> + /// <param name="parameterValues">array of objects holding the values to be assigned</param> + private void AssignParameterValues(string spName, IDbDataParameter[] commandParameters, object[] parameterValues) + { + if (commandParameters == null || parameterValues == null) + { + // Do nothing if we get no data. + // + return; + } + + var nValues = 0; + + // Iterate through the parameters, assigning the values from + // the corresponding position in the value array. + // + for (var index = 0; index < commandParameters.Length; index++) + { + var parameter = commandParameters[index]; + + if (_dataProvider.IsValueParameter(parameter)) + { + if (nValues >= parameterValues.Length) + throw new ArgumentException(string.Format("Parsing for {0} failed: {1}", spName, GetMissedColumnNames(index, commandParameters))); + + var value = parameterValues[nValues++]; + + _dataProvider.SetParameterValue(parameter, value ?? DBNull.Value); + } + } + + // We must have the same number of values as we pave parameters to put them in. + // + if (nValues != parameterValues.Length) + throw new ArgumentException(string.Format("Parsing for {0} failed: {1}", spName, GetExceedParameters(nValues, parameterValues))); + } + + string GetMissedColumnNames(int startIndex, IDbDataParameter[] commandParameters) + { + var columnNames = new List<string>(); + + for (var index = startIndex; index < commandParameters.Length; index++) + { + var parameter = commandParameters[index]; + + if (_dataProvider.IsValueParameter(parameter)) + { + columnNames.Add(string.Format("{0} {{{1}}}", parameter.ParameterName, parameter.DbType)); + } + } + +#if FW4 + return "Missed columns: " + string.Join(", ", columnNames); +#else + return "Missed columns: " + string.Join(", ", columnNames.ToArray()); +#endif + } + + static string GetExceedParameters(int startIndex, object[] parameterValues) + { + var columnNames = new List<string>(); + + for (var index = startIndex; index < parameterValues.Length; index++) + { + var parameter = parameterValues[index]; + columnNames.Add( + parameter == null + ? "<null>" + : string.Format("{0} {{{1}}}", parameter, parameter.GetType().Name)); + } + +#if FW4 + return "Exceed parameters: " + string.Join(", ", columnNames); +#else + return "Exceed parameters: " + string.Join(", ", columnNames.ToArray()); +#endif + } + + /// <overloads> + /// Assigns a business object to command parameters. + /// </overloads> + /// <summary> + /// Assigns the <see cref="DataRow"/> to command parameters. + /// </summary> + /// <include file="Examples1.xml" path='examples/db[@name="AssignParameterValues(DataRow)"]/*' /> + /// <remarks> + /// The method is used in addition to the <see cref="CreateParameters(object,IDbDataParameter[])"/> method. + /// </remarks> + /// <param name="dataRow">The <see cref="DataRow"/> to assign.</param> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + public DbManager AssignParameterValues(DataRow dataRow) + { + if (dataRow == null) + throw new ArgumentNullException("dataRow"); + + foreach (DataColumn c in dataRow.Table.Columns) + if (c.AutoIncrement == false && c.ReadOnly == false) + { + var o = dataRow[c.ColumnName]; + var name = _dataProvider.Convert(c.ColumnName, GetConvertTypeToParameter()).ToString(); + + Parameter(name).Value = + c.AllowDBNull && _mappingSchema.IsNull(o) ? DBNull.Value : o; + } + + if (_prepared) + InitParameters(CommandAction.Select); + + return this; + } + + /// <summary> + /// Assigns a business object to command parameters. + /// </summary> + /// <remarks> + /// The method is used in addition to the <see cref="CreateParameters(object,IDbDataParameter[])"/> method. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="AssignParameterValues(object)"]/*' /> + /// <param name="obj">An object to assign.</param> + /// <returns>This instance of the <see cref="DbManager"/>.</returns> + public DbManager AssignParameterValues(object obj) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + var om = _mappingSchema.GetObjectMapper(obj.GetType()); + + foreach (MemberMapper mm in om) + { + var name = _dataProvider.Convert(mm.Name, GetConvertTypeToParameter()).ToString(); + + if (Command.Parameters.Contains(name)) + { + var value = mm.GetValue(obj); + + _dataProvider.SetParameterValue( + Parameter(name), + value == null || mm.MapMemberInfo.Nullable && _mappingSchema.IsNull(value)? + DBNull.Value: value); + } + } + + if (_prepared) + InitParameters(CommandAction.Select); + + return this; + } + + private static Array SortArray(Array array, IComparer comparer) + { + if (array == null) + return null; + + var arrayClone = (Array)array.Clone(); + + Array.Sort(arrayClone, comparer); + + return arrayClone; + } + + /// <summary> + /// Creates an array of parameters from the <see cref="DataRow"/> object. + /// </summary> + /// <remarks> + /// The method can take an additional parameter list, + /// which can be created by using the same method. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="CreateParameters(DataRow,IDbDataParameter[])"]/*' /> + /// <param name="dataRow">The <see cref="DataRow"/> to create parameters.</param> + /// <param name="commandParameters">An array of parameters to be added to the result array.</param> + /// <returns>An array of parameters.</returns> + public IDbDataParameter[] CreateParameters( + DataRow dataRow, params IDbDataParameter[] commandParameters) + { + return CreateParameters(dataRow, null, null, null, commandParameters); + } + + /// <summary> + /// Creates an array of parameters from the <see cref="DataRow"/> object. + /// </summary> + /// <remarks> + /// The method can take an additional parameter list, + /// which can be created by using the same method. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="CreateParameters(DataRow,IDbDataParameter[])"]/*' /> + /// <param name="dataRow">The <see cref="DataRow"/> to create parameters.</param> + /// <param name="outputParameters">Output parameters names.</param> + /// <param name="inputOutputParameters">InputOutput parameters names.</param> + /// <param name="ignoreParameters">Parameters names to skip.</param> + /// <param name="commandParameters">An array of parameters to be added to the result array.</param> + /// <returns>An array of parameters.</returns> + public IDbDataParameter[] CreateParameters( + DataRow dataRow, + string[] outputParameters, + string[] inputOutputParameters, + string[] ignoreParameters, + params IDbDataParameter[] commandParameters) + { + if (dataRow == null) + throw new ArgumentNullException("dataRow"); + + var paramList = new ArrayList(); + IComparer comparer = CaseInsensitiveComparer.Default; + + outputParameters = (string[])SortArray(outputParameters, comparer); + inputOutputParameters = (string[])SortArray(inputOutputParameters, comparer); + ignoreParameters = (string[])SortArray(ignoreParameters, comparer); + + foreach (DataColumn c in dataRow.Table.Columns) + { + if (ignoreParameters != null && Array.BinarySearch(ignoreParameters, c.ColumnName, comparer) >= 0) + continue; + + if (c.AutoIncrement || c.ReadOnly) + continue; + + var name = _dataProvider.Convert(c.ColumnName, GetConvertTypeToParameter()).ToString(); + var parameter = + c.AllowDBNull + ? NullParameter(name, dataRow[c.ColumnName]) + : Parameter (name, dataRow[c.ColumnName]); + + if (outputParameters != null && Array.BinarySearch(outputParameters, c.ColumnName, comparer) >= 0) + parameter.Direction = ParameterDirection.Output; + else if (inputOutputParameters != null && Array.BinarySearch(inputOutputParameters, c.ColumnName, comparer) >= 0) + parameter.Direction = ParameterDirection.InputOutput; + + paramList.Add(parameter); + } + + if (commandParameters != null) + paramList.AddRange(commandParameters); + + return (IDbDataParameter[])paramList.ToArray(typeof(IDbDataParameter)); + } + + /// <summary> + /// Creates an array of parameters from a business object. + /// </summary> + /// <remarks> + /// The method can take an additional parameter list, + /// which can be created by using the same method. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="CreateParameters(object,IDbDataParameter[])"]/*' /> + /// <param name="obj">An object.</param> + /// <param name="commandParameters">An array of parameters to be added to the result array.</param> + /// <returns>An array of parameters.</returns> + public IDbDataParameter[] CreateParameters( + object obj, + params IDbDataParameter[] commandParameters) + { + return CreateParameters(obj, null, null, null, commandParameters); + } + + /// <summary> + /// Creates an array of parameters from a business object. + /// </summary> + /// <remarks> + /// The method can take an additional parameter list, + /// which can be created by using the same method. + /// </remarks> + /// <include file="Examples.xml" path='examples/db[@name="CreateParameters(object,IDbDataParameter[])"]/*' /> + /// <param name="obj">An object.</param> + /// <param name="outputParameters">Output parameters names.</param> + /// <param name="inputOutputParameters">InputOutput parameters names.</param> + /// <param name="ignoreParameters">Parameters names to skip.</param> + /// <param name="commandParameters">An array of parameters to be added to the result array.</param> + /// <returns>An array of parameters.</returns> + public IDbDataParameter[] CreateParameters( + object obj, + string[] outputParameters, + string[] inputOutputParameters, + string[] ignoreParameters, + params IDbDataParameter[] commandParameters) + { + if (obj == null) + throw new ArgumentNullException("obj"); + + var isType = obj is Type; + var type = isType? (Type)obj: obj.GetType(); + var om = _mappingSchema.GetObjectMapper(type); + var paramList = new ArrayList(); + var comparer = CaseInsensitiveComparer.Default; + + outputParameters = (string[])SortArray(outputParameters, comparer); + inputOutputParameters = (string[])SortArray(inputOutputParameters, comparer); + ignoreParameters = (string[])SortArray(ignoreParameters, comparer); + + foreach (MemberMapper mm in om) + { + if (ignoreParameters != null && Array.BinarySearch(ignoreParameters, mm.Name, comparer) >= 0) + continue; + + var value = isType? null: mm.GetValue(obj); + var name = _dataProvider.Convert(mm.Name, GetConvertTypeToParameter()).ToString(); + + var parameter = + value == null ? + NullParameter(name, value, mm.MapMemberInfo.NullValue) : + (mm.DbType != DbType.Object) ? + Parameter(name, value, mm.DbType): + Parameter(name, value); + + if (outputParameters != null && Array.BinarySearch(outputParameters, mm.Name, comparer) >= 0) + parameter.Direction = ParameterDirection.Output; + else if (inputOutputParameters != null && Array.BinarySearch(inputOutputParameters, mm.Name, comparer) >= 0) + parameter.Direction = ParameterDirection.InputOutput; + + paramList.Add(parameter); + } + + if (commandParameters != null) + paramList.AddRange(commandParameters); + + return (IDbDataParameter[])paramList.ToArray(typeof(IDbDataParameter)); + } + + /// <summary> + /// Maps all parameters returned from the server to all given objects. + /// </summary> + /// <param name="returnValueMember">Name of a <see cref="MemberMapper"/> to map return value.</param> + /// <param name="obj">An <see cref="System.Object"/> to map from command parameters.</param> + public void MapOutputParameters( + string returnValueMember, + object obj) + { + var dest = _mappingSchema.GetDataDestination(obj); + + foreach (IDbDataParameter parameter in Command.Parameters) + { + var ordinal = -1; + + switch (parameter.Direction) + { + case ParameterDirection.InputOutput: + case ParameterDirection.Output: + ordinal = dest.GetOrdinal( + _dataProvider.Convert(parameter.ParameterName, ConvertType.SprocParameterToName).ToString()); + break; + + case ParameterDirection.ReturnValue: + + if (returnValueMember != null) + { + if (!returnValueMember.StartsWith("@") && dest is ObjectMapper) + { + var om = (ObjectMapper) dest; + var ma = om.TypeAccessor[returnValueMember]; + + if (ma != null) + { + ma.SetValue(obj, _mappingSchema.ConvertChangeType(parameter.Value, ma.Type)); + continue; + } + } + else + returnValueMember = returnValueMember.Substring(1); + + ordinal = dest.GetOrdinal(returnValueMember); + } + + break; + } + + if (ordinal >= 0) + dest.SetValue(obj, ordinal, _mappingSchema.ConvertChangeType(parameter.Value, dest.GetFieldType(ordinal))); + } + } + + /// <summary> + /// Maps all parameters returned from the server to an object. + /// </summary> + /// <param name="obj">An <see cref="System.Object"/> to map from command parameters.</param> + public void MapOutputParameters(object obj) + { + MapOutputParameters(null, obj); + } + + /// <summary> + /// Maps all parameters returned from the server to all given objects. + /// </summary> + /// <param name="returnValueMember">Name of the member used to map the + /// return value. Can be null.</param> + /// <param name="objects">An array of <see cref="System.Object"/> to map + /// from command parameters.</param> + public void MapOutputParameters(string returnValueMember, params object[] objects) + { + if (objects == null) + return; + + foreach (var obj in objects) + MapOutputParameters(returnValueMember, obj); + } + + /// <summary> + /// Maps all parameters returned from the server to an object. + /// </summary> + /// <param name="objects">An array of <see cref="System.Object"/> to map + /// from command parameters.</param> + public void MapOutputParameters(params object[] objects) + { + MapOutputParameters(null, objects); + } + + /// <overloads> + /// Adds a parameter to the <see cref="Command"/> or returns existing one. + /// </overloads> + /// <summary> + /// Returns an existing parameter. + /// </summary> + /// <remarks> + /// The method can be used to retrieve return and output parameters. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Parameter(string)"]/*' /> + /// <param name="parameterName">The name of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter(string parameterName) + { + return _dataProvider.GetParameter(Command, parameterName); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Parameter(string,object)"]/*' /> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter(string parameterName, object value) + { + return Parameter(ParameterDirection.Input, parameterName, value); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter(string parameterName, DbType dbType) + { + return Parameter(ParameterDirection.Input, parameterName, dbType); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter(string parameterName, DbType dbType, int size) + { + return Parameter(ParameterDirection.Input, parameterName, dbType, size); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// If the parameter is null, it's converted to <see cref="DBNull"/>.<see cref="DBNull.Value"/>. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="NullParameter(string,object)"]/*' /> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter NullParameter(string parameterName, object value) + { + if (_mappingSchema.IsNull(value)) + @value = DBNull.Value; + + return Parameter(ParameterDirection.Input, parameterName, value); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="nullValue">The null equivalent to compare with the value.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter NullParameter(string parameterName, object value, object nullValue) + { + if (value == null || value.Equals(nullValue)) + @value = DBNull.Value; + + return Parameter(ParameterDirection.Input, parameterName, value); + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter InputParameter(string parameterName, object value) + { + return Parameter(ParameterDirection.Input, parameterName, value); + } + + /// <summary> + /// Adds an output parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Output</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter OutputParameter(string parameterName, object value) + { + return Parameter(ParameterDirection.Output, parameterName, value); + } + + /// <summary> + /// Adds an output parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Output</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter OutputParameter(string parameterName, DbType dbType) + { + return Parameter(ParameterDirection.Output, parameterName, dbType); + } + + /// <summary> + /// Adds an output parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Output</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter OutputParameter(string parameterName, DbType dbType, int size) + { + return Parameter(ParameterDirection.Output, parameterName, dbType, size); + } + + /// <summary> + /// Adds an input-output parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.InputOutput</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter InputOutputParameter(string parameterName, object value) + { + return Parameter(ParameterDirection.InputOutput,parameterName, value); + } + + /// <summary> + /// Adds a return value parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.ReturnValue</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter ReturnValue(string parameterName) + { + return Parameter(ParameterDirection.ReturnValue, parameterName, null); + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + object value) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + + _dataProvider.SetParameterValue(parameter, value ?? DBNull.Value); + + return parameter; + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + object value, + DbType dbType) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + parameter.DbType = _dataProvider.GetParameterDbType(dbType); + + _dataProvider.SetParameterValue(parameter, value ?? DBNull.Value); + + return parameter; + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + object value, + DbType dbType) + { + return Parameter(ParameterDirection.Input, parameterName, value, dbType); + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + object value, + DbType dbType, + int size) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + parameter.DbType = dbType; + parameter.Size = size; + + _dataProvider.SetParameterValue(parameter, value); + + return parameter; + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="typeName">User defined type name for a table-valued parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + object value, + string typeName) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + _dataProvider.SetUserDefinedType(parameter, typeName); + _dataProvider.SetParameterValue (parameter, value); + + return parameter; + } + + /// <summary> + /// Adds an input parameter to the <see cref="Command"/>. + /// </summary> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="value">The <see cref="System.Object"/> + /// that is the value of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + object value, + DbType dbType, + int size) + { + return Parameter(ParameterDirection.Input, parameterName, value, dbType, size); + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + DbType dbType) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + parameter.DbType = dbType; + + return parameter; + } + + /// <summary> + /// Adds a parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the specified + /// <see cref="System.Data.ParameterDirection"/> type. + /// </remarks> + /// <param name="parameterDirection">One of the <see cref="System.Data.ParameterDirection"/> values.</param> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <seealso cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + ParameterDirection parameterDirection, + string parameterName, + DbType dbType, + int size) + { + var parameter = _dataProvider.CreateParameterObject(Command); + + parameter.ParameterName = parameterName; + parameter.Direction = parameterDirection; + parameter.DbType = dbType; + parameter.Size = size; + + return parameter; + } + + /// <summary> + /// Creates an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type + /// and <see cref="System.Data.DataRowVersion">DataRowVersion.Current</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <param name="sourceColumn">Source column for a parameter in the <see cref="DataTable"/>.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + DbType dbType, + int size, + string sourceColumn) + { + var param = Parameter(ParameterDirection.Input, parameterName, dbType, size); + + param.SourceColumn = sourceColumn; + param.SourceVersion = DataRowVersion.Current; + + return param; + } + + /// <summary> + /// Creates an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type + /// and <see cref="System.Data.DataRowVersion">DataRowVersion.Current</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="sourceColumn">Source column for a parameter in the <see cref="DataTable"/>.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + DbType dbType, + string sourceColumn) + { + var param = Parameter(ParameterDirection.Input, parameterName, dbType); + + param.SourceColumn = sourceColumn; + param.SourceVersion = DataRowVersion.Current; + + return param; + } + + /// <summary> + /// Creates an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type + /// and <see cref="System.Data.DataRowVersion">DataRowVersion.Current</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="size">Size of the parameter.</param> + /// <param name="sourceColumn">Source column for a parameter in the <see cref="DataTable"/>.</param> + /// <param name="dataRowVersion">Version of data to use for a parameter in the <see cref="DataTable"/>.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + DbType dbType, + int size, + string sourceColumn, + DataRowVersion dataRowVersion) + { + var param = Parameter(ParameterDirection.Input, parameterName, dbType, size); + + param.SourceColumn = sourceColumn; + param.SourceVersion = dataRowVersion; + + return param; + } + + /// <summary> + /// Creates an input parameter to the <see cref="Command"/>. + /// </summary> + /// <remarks> + /// The method creates a parameter with the + /// <see cref="System.Data.ParameterDirection">ParameterDirection.Input</see> type + /// and <see cref="System.Data.DataRowVersion">DataRowVersion.Current</see> type. + /// </remarks> + /// <param name="parameterName">The name of the parameter.</param> + /// <param name="dbType">One of the <see cref="DbType"/> values.</param> + /// <param name="sourceColumn">Source column for a parameter in the <see cref="DataTable"/>.</param> + /// <param name="dataRowVersion">Version of data to use for a parameter in the <see cref="DataTable"/>.</param> + /// <returns>The <see cref="IDbDataParameter"/> object.</returns> + public IDbDataParameter Parameter( + string parameterName, + DbType dbType, + string sourceColumn, + DataRowVersion dataRowVersion) + { + var param = Parameter(ParameterDirection.Input, parameterName, dbType); + + param.SourceColumn = sourceColumn; + param.SourceVersion = dataRowVersion; + + return param; + } + + public ConvertType GetConvertTypeToParameter() + { + return Command.CommandType == CommandType.StoredProcedure ? + ConvertType.NameToSprocParameter: + ConvertType.NameToCommandParameter; + } + + #endregion + + #region SetCommand + + /// <summary> + /// Specifies the action that command is supposed to perform, i.e. Select, Insert, Update, Delete. + /// It is used in Execute methods of the <see cref="DbManager"/> class to identify command instance + /// to be used. + /// </summary> + enum CommandAction + { + Select, + Insert, + Update, + Delete + } + + private bool _executed; + private bool _prepared; + + private IDbDataParameter[] _selectCommandParameters; + private IDbDataParameter[] _insertCommandParameters; + private IDbDataParameter[] _updateCommandParameters; + private IDbDataParameter[] _deleteCommandParameters; + + private void SetCommand(CommandAction commandAction, IDbCommand command) + { + switch (commandAction) + { + case CommandAction.Select: _selectCommand = command; break; + case CommandAction.Insert: _insertCommand = command; break; + case CommandAction.Update: _updateCommand = command; break; + case CommandAction.Delete: _deleteCommand = command; break; + } + } + + private IDbCommand GetCommand(CommandAction commandAction) + { + switch (commandAction) + { + default: + //case CommandAction.Select: + return SelectCommand; + case CommandAction.Insert: return InsertCommand; + case CommandAction.Update: return UpdateCommand; + case CommandAction.Delete: return DeleteCommand; + } + } + + private IDbCommand GetCommand(CommandAction commandAction, CommandType commandType) + { + IDbCommand command; + + switch (commandAction) + { + default : command = _selectCommand; break; + case CommandAction.Insert : command = _insertCommand; break; + case CommandAction.Update : command = _updateCommand; break; + case CommandAction.Delete : command = _deleteCommand; break; + } + + if (command != null && !DataProvider.CanReuseCommand(command, commandType)) + { + command.Dispose(); + + switch (commandAction) + { + default : _selectCommand = null; break; + case CommandAction.Insert : _insertCommand = null; break; + case CommandAction.Update : _updateCommand = null; break; + case CommandAction.Delete : _deleteCommand = null; break; + } + } + + return GetCommand(commandAction); + } + + private void SetCommandParameters(CommandAction commandAction, IDbDataParameter[] commandParameters) + { + switch (commandAction) + { + case CommandAction.Select: _selectCommandParameters = commandParameters; break; + case CommandAction.Insert: _insertCommandParameters = commandParameters; break; + case CommandAction.Update: _updateCommandParameters = commandParameters; break; + case CommandAction.Delete: _deleteCommandParameters = commandParameters; break; + } + } + + private IDbDataParameter[] GetCommandParameters(CommandAction commandAction) + { + switch (commandAction) + { + default: + //case CommandAction.Select: + return _selectCommandParameters; + case CommandAction.Insert: return _insertCommandParameters; + case CommandAction.Update: return _updateCommandParameters; + case CommandAction.Delete: return _deleteCommandParameters; + } + } + + private DbManager SetCommand( + CommandAction commandAction, + CommandType commandType, + string commandText, + params IDbDataParameter[] commandParameters) + { + if (_executed) + { + _executed = false; + _prepared = false; + } + + PrepareCommand(commandAction, commandType, commandText, commandParameters); + + return this; + } + + private DbManager SetSpCommand( + CommandAction commandAction, + string spName, + bool openNewConnectionToDiscoverParameters, + params object[] parameterValues) + { + return SetCommand( + commandAction, + CommandType.StoredProcedure, + spName, + CreateSpParameters(spName, parameterValues, openNewConnectionToDiscoverParameters)); + } + + private DbManager SetSpCommand( + CommandAction commandAction, + string spName, + params object[] parameterValues) + { + return SetCommand( + commandAction, + CommandType.StoredProcedure, + spName, + CreateSpParameters(spName, parameterValues, Configuration.OpenNewConnectionToDiscoverParameters)); + } + + #region Select + + /// <summary> + /// Creates a SQL statement. + /// </summary> + /// <param name="commandText">The command text to execute.</param> + /// <returns>Current instance.</returns> + public DbManager SetCommand( + string commandText) + { + return SetCommand(CommandAction.Select, CommandType.Text, commandText, null); + } + + /// <summary> + /// Creates a SQL statement. + /// </summary> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> (stored procedure, text, etc.)</param> + /// <param name="commandText">The command text to execute.</param> + /// <returns>Current instance.</returns> + public DbManager SetCommand( + CommandType commandType, + string commandText) + { + return SetCommand(CommandAction.Select, commandType, commandText, null); + } + + /// <summary> + /// Creates a SQL statement. + /// </summary> + /// <remarks> + /// The method can be used to create the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetCommand( + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand(CommandAction.Select, CommandType.Text, commandText, commandParameters); + } + + /// <summary> + /// Creates a SQL statement. + /// </summary> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> (stored procedure, text, etc.)</param> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetCommand( + CommandType commandType, + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand(CommandAction.Select, commandType, commandText, commandParameters); + } + + /// <summary> + /// Creates a command to be executed as a stored procedure using the provided parameter values. + /// </summary> + /// <remarks> + /// The method queries the database to discover the parameters for the stored procedure + /// (the first time each stored procedure is called), + /// and assign the values based on parameter order. + /// </remarks> + /// <param name="spName">The name of the stored procedure</param> + /// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param> + /// <returns>Current instance.</returns> + public DbManager SetSpCommand( + string spName, + params object[] parameterValues) + { + return SetSpCommand(CommandAction.Select, spName, parameterValues); + } + + public DbManager SetSpCommand( + string spName, + bool openNewConnectionToDiscoverParameters, + params object[] parameterValues) + { + return SetSpCommand(CommandAction.Select, spName, openNewConnectionToDiscoverParameters, parameterValues); + } + + public DbManager SetCommand(SqlQuery sql, params IDbDataParameter[] commandParameters) + { + var sb = new StringBuilder(); + + DataProvider.CreateSqlProvider().BuildSql(0, sql, sb, 0, 0, false); + + var command = sb.ToString(); + + if (TraceSwitch.TraceInfo) + { + var info = string.Format("{0} {1}\n{2}", DataProvider.Name, ConfigurationString, command); + + if (commandParameters != null && commandParameters.Length > 0) + foreach (var p in commandParameters) + info += string.Format("\n{0}\t{1}", p.ParameterName, p.Value); + + WriteTraceLine(info, TraceSwitch.DisplayName); + } + + return SetCommand(command, commandParameters); + } + + #endregion + + #region Insert + + /// <summary> + /// Creates an Insert SQL statement. + /// </summary> + /// <remarks> + /// The method can be used to create the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetInsertCommand( + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Insert, CommandType.Text, commandText, commandParameters); + } + + /// <summary> + /// Creates an Insert SQL statement. + /// </summary> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> (stored procedure, text, etc.)</param> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetInsertCommand( + CommandType commandType, + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Insert, commandType, commandText, commandParameters); + } + + /// <summary> + /// Creates an Insert command to be executed as a stored procedure using the provided parameter values. + /// </summary> + /// <remarks> + /// The method queries the database to discover the parameters for the stored procedure + /// (the first time each stored procedure is called), + /// and assign the values based on parameter order. + /// </remarks> + /// <param name="spName">The name of the stored procedure</param> + /// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param> + /// <returns>Current instance.</returns> + public DbManager SetInsertSpCommand( + string spName, + params object[] parameterValues) + { + return SetSpCommand(CommandAction.Insert, spName, parameterValues); + } + + #endregion + + #region Update + + /// <summary> + /// Creates an Update SQL statement. + /// </summary> + /// <remarks> + /// The method can be used to create the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetUpdateCommand( + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Update, CommandType.Text, commandText, commandParameters); + } + + /// <summary> + /// Creates an Update SQL statement. + /// </summary> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> (stored procedure, text, etc.)</param> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetUpdateCommand( + CommandType commandType, + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Update, commandType, commandText, commandParameters); + } + + /// <summary> + /// Creates an Update command to be executed as a stored procedure using the provided parameter values. + /// </summary> + /// <remarks> + /// The method queries the database to discover the parameters for the stored procedure + /// (the first time each stored procedure is called), + /// and assign the values based on parameter order. + /// </remarks> + /// <param name="spName">The name of the stored procedure</param> + /// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param> + /// <returns>Current instance.</returns> + public DbManager SetUpdateSpCommand( + string spName, + params object[] parameterValues) + { + return SetSpCommand(CommandAction.Update, spName, parameterValues); + } + + #endregion + + #region Delete + + /// <summary> + /// Creates a Delete SQL statement. + /// </summary> + /// <remarks> + /// The method can be used to create the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetDeleteCommand( + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Delete, CommandType.Text, commandText, commandParameters); + } + + /// <summary> + /// Creates a Delete SQL statement. + /// </summary> + /// <param name="commandType">The <see cref="System.Data.CommandType"/> (stored procedure, text, etc.)</param> + /// <param name="commandText">The command text to execute.</param> + /// <param name="commandParameters">An array of parameters used to executes the command.</param> + /// <returns>Current instance.</returns> + public DbManager SetDeleteCommand( + CommandType commandType, + string commandText, + params IDbDataParameter[] commandParameters) + { + return SetCommand( + CommandAction.Delete, commandType, commandText, commandParameters); + } + + /// <summary> + /// Creates a Delete command to be executed as a stored procedure using the provided parameter values. + /// </summary> + /// <remarks> + /// The method queries the database to discover the parameters for the stored procedure + /// (the first time each stored procedure is called), + /// and assign the values based on parameter order. + /// </remarks> + /// <param name="spName">The name of the stored procedure</param> + /// <param name="parameterValues">An array of objects to be assigned as the input values of the stored procedure</param> + /// <returns>Current instance.</returns> + public DbManager SetDeleteSpCommand( + string spName, + params object[] parameterValues) + { + return SetSpCommand(CommandAction.Delete, spName, parameterValues); + } + + #endregion + + #endregion + + #region Prepare + + private void PrepareCommand( + CommandAction commandAction, + CommandType commandType, + string commandText, + IDbDataParameter[] commandParameters) + { + DataProvider.PrepareCommand(ref commandType, ref commandText, ref commandParameters); + + LastQuery = commandText; + + var command = GetCommand(commandAction, commandType, commandText); + + SetCommand (commandAction, command); + SetCommandParameters(commandAction, commandParameters); + + if (commandParameters != null) + { + AttachParameters(command, commandParameters); + } + } + + /// <summary> + /// Prepares a command for execution. + /// </summary> + /// <returns>Current instance.</returns> + public DbManager Prepare() + { + var command = GetCommand(CommandAction.Select); + + if (InitParameters(CommandAction.Select) == false) + ExecuteOperation(OperationType.PrepareCommand, command.Prepare); + + _prepared = true; + + return this; + } + + bool InitParameters(CommandAction commandAction) + { + var prepare = false; + + var commandParameters = GetCommandParameters(commandAction); + + if (commandParameters != null) + { + foreach (var p in commandParameters) + { + if (_dataProvider.InitParameter(p)) + continue; + + // It forces parameter's filed 'MetaType' to be set. + // Same for p.Size = p.Size below. + // + p.DbType = p.DbType; + + if (p.Value is string) + { + var len = ((string)p.Value).Length; + + if (p.Size < len) + { + p.Size = len; + prepare = true; + } + else + p.Size = p.Size; + } + else if (p.Value is DBNull) + { + p.Size = 1; + } + else if (p.Value is byte[]) + { + var len = ((byte[])p.Value).Length; + + if (p.Size < len) + { + p.Size = len; + prepare = true; + } + else + p.Size = p.Size; + } + else if (p.Value is char[]) + { + var len = ((char[])p.Value).Length; + + if (p.Size < len) + { + p.Size = len; + prepare = true; + } + else + p.Size = p.Size; + } + else if (p.Value is decimal) + { + SqlDecimal d = (decimal)p.Value; + + if (p.Precision < d.Precision) + { + p.Precision = d.Precision; + prepare = true; + } + else + p.Precision = p.Precision; + + if (p.Scale < d.Scale) + { + p.Scale = d.Scale; + prepare = true; + } + else + p.Scale = p.Scale; + } + } + + // Re-prepare command to avoid truncation. + // + if (prepare) + { + var command = GetCommand(commandAction); + + AttachParameters(command, commandParameters); + command.Prepare(); + } + } + + return prepare; + } + + #endregion + + #region ExecuteForEach + + /// <summary> + /// Executes a SQL statement for a given collection of objects and + /// returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method prepares the <see cref="Command"/> object + /// and calls the <see cref="ExecuteNonQuery()"/> method for each item of the list. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Execute(CommandType,string,IList)"]/*' /> + /// <param name="collection">The list of objects used to execute the command.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteForEach(ICollection collection) + { + var rowsTotal = 0; + + if (collection != null && collection.Count != 0) + { + var initParameters = true; + + foreach (var o in collection) + { + if (initParameters) + { + initParameters = false; + + var parameters = GetCommandParameters(CommandAction.Select); + + if (parameters == null || parameters.Length == 0) + { + parameters = CreateParameters(o); + + SetCommandParameters(CommandAction.Select, parameters); + AttachParameters(SelectCommand, parameters); + Prepare(); + } + } + + AssignParameterValues(o); + rowsTotal += ExecuteNonQueryInternal(); + MapOutputParameters(o); + } + } + + return rowsTotal; + } + + /// <summary> + /// Executes a SQL statement for a given collection of objects and + /// returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method prepares the <see cref="Command"/> object + /// and calls the <see cref="ExecuteNonQuery()"/> method for each item of the list. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Execute(CommandType,string,IList)"]/*' /> + /// <param name="collection">The list of objects used to execute the command.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteForEach<T>(ICollection<T> collection) + { + var rowsTotal = 0; + + if (collection != null && collection.Count != 0) + { + var initParameters = true; + + foreach (var o in collection) + { + if (initParameters) + { + initParameters = false; + + var parameters = GetCommandParameters(CommandAction.Select); + + if (parameters == null || parameters.Length == 0) + { + parameters = CreateParameters(o); + + SetCommandParameters(CommandAction.Select, parameters); + AttachParameters(SelectCommand, parameters); + Prepare(); + } + } + + AssignParameterValues(o); + rowsTotal += ExecuteNonQueryInternal(); + MapOutputParameters(o); + } + } + + return rowsTotal; + } + + public int ExecuteForEach<T>(int maxBatchSize, IEnumerable<T> collection) + { + var om = _mappingSchema.GetObjectMapper(typeof(T)); + var mms = new List<MemberMapper>(); + + foreach (MemberMapper mm in om) + { + var name = _dataProvider.Convert(mm.Name, GetConvertTypeToParameter()).ToString(); + + if (Command.Parameters.Contains(name)) + mms.Add(mm); + } + + return + ExecuteForEach( + collection, + mms.ToArray(), + maxBatchSize, + obj => CreateParameters(obj)); + } + + public delegate IDbDataParameter[] ParameterProvider<T>(T obj); + + internal int ExecuteForEach<T>(IEnumerable<T> collection, MemberMapper[] members, int maxBatchSize, ParameterProvider<T> getParameters) + { + if (collection == null) + return 0; + + var maxRows = + Math.Max( + Math.Min( + Math.Max( + members.Length == 0? 1000 : _dataProvider.MaxParameters / members.Length, + members.Length), + maxBatchSize), + 1); + var baseSql = SelectCommand.CommandText; + var paramName = _dataProvider.Convert(".", ConvertType.NameToQueryParameter).ToString(); + var rowsTotal = 0; + var nRows = 0; + var initParameters = true; + var saveCanRaseEvent = _canRaiseEvents; + + _canRaiseEvents = false; + + var sb = new StringBuilder(); + var rowSql = new List<int>(maxRows); + IDbDataParameter[] baseParameters = null; + var parameters = new List<IDbDataParameter>(); + var hasValue = new List<bool>(); + + var isPrepared = false; + + foreach (var obj in collection) + { + if (initParameters) + { + initParameters = false; + baseParameters = getParameters(obj); + + if (maxRows != 1) + { + var n = 0; + + foreach (var p in baseParameters) + n += p.ParameterName.Length + 3 - "{0}".Length + _dataProvider.EndOfSql.Length; + + maxRows = Math.Max(1, Math.Min(maxRows, _dataProvider.MaxBatchSize / (baseSql.Length + n))); + } + + if (maxRows != 1) + baseSql += _dataProvider.EndOfSql; + } + + if (rowSql.Count < maxRows) + { +// ReSharper disable AccessToModifiedClosure + Converter<IDbDataParameter,string> c1 = p => p.ParameterName + nRows; +// ReSharper restore AccessToModifiedClosure + Converter<IDbDataParameter,string> c2 = p => p.ParameterName; + + sb + .Append("\n") + .AppendFormat( + baseSql, + Array.ConvertAll( + baseParameters, + baseParameters.Length > 0 && baseParameters[0].ParameterName != paramName? c1 : c2)); + + rowSql.Add(sb.Length); + + for (var i = 0; i < members.Length; i++) + { + var value = members[i].GetValue(obj); + var type = members[i].MemberAccessor.Type; + var dbType = members[i].GetDbType(); + + IDbDataParameter p; + + if ((value == null || value == DBNull.Value) && (dbType == DbType.Binary || type == typeof(byte[])) || + type == typeof(System.Data.Linq.Binary)) + { + p = Parameter(baseParameters[i].ParameterName + nRows, DBNull.Value, DbType.Binary); + } + else + { + if (value != null && value.GetType().IsEnum) + value = MappingSchema.MapEnumToValue(value, true); + + p = value != null + ? Parameter(baseParameters[i].ParameterName + nRows, value) + : Parameter(baseParameters[i].ParameterName + nRows, DBNull.Value, members[i].GetDbType()); + } + + parameters.Add(p); + hasValue.Add(value != null); + } + } + else + { + var n = nRows * members.Length; + + for (var i = 0; i < members.Length; i++) + { + var value = members[i].GetValue(obj); + + if (!hasValue[n + i] && value != null) + { + isPrepared = false; + + var type = members[i].MemberAccessor.Type; + var dbType = members[i].GetDbType(); + + if (value.GetType().IsEnum) + value = MappingSchema.MapEnumToValue(value, true); + + IDbDataParameter p; + if (dbType != DbType.Object) + p = Parameter(baseParameters[i].ParameterName + nRows, value ?? DBNull.Value, dbType); + else + p = Parameter(baseParameters[i].ParameterName + nRows, value ?? DBNull.Value/*, dbType*/); + + parameters[n + i] = p; + hasValue [n + i] = true; + } + else + { + if (value != null && value.GetType().IsEnum) + value = MappingSchema.MapEnumToValue(value, true); + + _dataProvider.SetParameterValue( + parameters[n + i], + value ?? DBNull.Value); + //value == null || members[i].MapMemberInfo.Nullable && _mappingSchema.IsNull(value) + // ? DBNull.Value + // : value); + } + + } + } + + nRows++; + + if (nRows >= maxRows) + { + if (!isPrepared) + { + SetCommand(sb.ToString(), parameters.ToArray()); + Prepare(); + isPrepared = true; + } + else + { + InitParameters(CommandAction.Select); + } + + var n = ExecuteNonQueryInternal(); + if (n > 0) + rowsTotal += n; + + nRows = 0; + } + } + + if (nRows > 0) + { + if (rowSql.Count >= maxRows) + { + var nps = nRows * members.Length; + parameters.RemoveRange(nps, parameters.Count - nps); + + sb.Length = rowSql[nRows - 1]; + } + + SetCommand(sb.ToString(), parameters.ToArray()); + Prepare(); + + var n = ExecuteNonQueryInternal(); + if (n > 0) + rowsTotal += n; + } + + _canRaiseEvents = saveCanRaseEvent; + + return rowsTotal; + } + + /// <summary> + /// Executes a SQL statement for the <see cref="DataTable"/> and + /// returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method prepares the <see cref="Command"/> object + /// and calls the <see cref="ExecuteNonQuery()"/> method for each item + /// of the <see cref="DataTable"/>. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Execute(CommandType,string,DataTable)"]/*' /> + /// <param name="table">An instance of the <see cref="DataTable"/> class to execute the command.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteForEach(DataTable table) + { + var rowsTotal = 0; + + if (table != null && table.Rows.Count != 0) + { + var parameters = GetCommandParameters(CommandAction.Select); + + if (parameters == null || parameters.Length == 0) + { + parameters = CreateParameters(table.Rows[0]); + + SetCommandParameters(CommandAction.Select, parameters); + AttachParameters(SelectCommand, parameters); + Prepare(); + } + + foreach (DataRow dr in table.Rows) + { + AssignParameterValues(dr); + rowsTotal += ExecuteNonQueryInternal(); + } + } + + return rowsTotal; + } + + /// <summary> + /// Executes a SQL statement for the first table of the <see cref="DataSet"/> + /// and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method prepares the <see cref="Command"/> object + /// and calls the <see cref="ExecuteNonQuery()"/> method for each item of the first table. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Execute(CommandType,string,DataSet)"]/*' /> + /// <param name="dataSet">An instance of the <see cref="DataSet"/> class to execute the command.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteForEach(DataSet dataSet) + { + return ExecuteForEach(dataSet.Tables[0]); + } + + /// <summary> + /// Executes a SQL statement for the specified table of the <see cref="DataSet"/> + /// and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method prepares the <see cref="Command"/> object + /// and calls the <see cref="ExecuteNonQuery()"/> method for each item of the first table. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="Execute(CommandType,string,DataSet,string)"]/*' /> + /// <param name="dataSet">An instance of the <see cref="DataSet"/> class to execute the command.</param> + /// <param name="nameOrIndex">The table name or index. + /// name/index.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteForEach(DataSet dataSet, NameOrIndexParameter nameOrIndex) + { + return nameOrIndex.ByName ? ExecuteForEach(dataSet.Tables[nameOrIndex.Name]) + : ExecuteForEach(dataSet.Tables[nameOrIndex.Index]); + } + + #endregion + + #region ExecuteNonQuery + + /// <summary> + /// Executes a SQL statement and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method can be used to execute the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <include file="Examples1.xml" path='examples/db[@name="ExecuteNonQuery()"]/*' /> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteNonQuery() + { + if (_prepared) + InitParameters(CommandAction.Select); + + return ExecuteNonQueryInternal(); + } + + /// <summary> + /// Executes a SQL statement and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method can be used to execute the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="returnValueMember">Name of a <see cref="MemberMapper"/> to map return value.</param> + /// <param name="obj">An <see cref="System.Object"/> to map from command parameters.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteNonQuery( + string returnValueMember, + object obj) + { + var rowsAffected = ExecuteNonQuery(); + + MapOutputParameters(returnValueMember, obj); + + return rowsAffected; + } + + /// <summary> + /// Executes a SQL statement and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method can be used to execute the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="obj">An <see cref="System.Object"/> to map from command parameters.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteNonQuery(object obj) + { + var rowsAffected = ExecuteNonQuery(); + + MapOutputParameters(null, obj); + + return rowsAffected; + } + + /// <summary> + /// Executes a SQL statement and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method can be used to execute the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="returnValueMember">Name of a <see cref="MemberMapper"/> to map return value.</param> + /// <param name="objects">An array of <see cref="System.Object"/> to map + /// from command parameters.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteNonQuery( + string returnValueMember, + params object[] objects) + { + var rowsAffected = ExecuteNonQuery(); + + MapOutputParameters(returnValueMember, objects); + + return rowsAffected; + } + + /// <summary> + /// Executes a SQL statement and returns the number of rows affected. + /// </summary> + /// <remarks> + /// The method can be used to execute the <i>INSERT</i>, <i>UPDATE</i>, and <i>DELETE</i> SQL statements. + /// </remarks> + /// <param name="objects">An array of <see cref="System.Object"/> to map + /// from command parameters.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteNonQuery(params object[] objects) + { + var rowsAffected = ExecuteNonQuery(); + + MapOutputParameters(null, objects); + + return rowsAffected; + } + + /// <summary> + /// Executes several SQL statements at a time using single roundtrip to the server (if supported by data provider). + /// </summary> + /// <remarks> + /// All parameters of the query must be arrays of type corresponding to the type of the parameter. + /// The value of the <paramref name="iterations"/> parameter must be equal to the number of elements of each array. + /// </remarks> + /// <param name="iterations">The number of iterations.</param> + /// <returns>The number of rows affected by the command.</returns> + public int ExecuteArray(int iterations) + { + return ExecuteOperation<int>(OperationType.ExecuteNonQuery, () => DataProvider.ExecuteArray(SelectCommand, iterations)); + } + + #endregion + + #region ExecuteScalar + + /// <summary> + /// Executes the query, and returns the first column of the first row + /// in the resultset returned by the query. Extra columns or rows are + /// ignored. + /// </summary> + /// <returns>The first column of the first row in the resultset.</returns> + /// <seealso cref="ExecuteScalar(ScalarSourceType, NameOrIndexParameter)"/> + public object ExecuteScalar() + { + if (_prepared) + InitParameters(CommandAction.Select); + + using (var rd = ExecuteReaderInternal(CommandBehavior.Default)) + return rd.Read() && rd.FieldCount > 0 ? rd.GetValue(0) : null; + } + + /// <summary> + /// Executes the query, and returns the value with specified scalar + /// source type. + /// </summary> + /// <param name="sourceType">The method used to return the scalar + /// value.</param> + /// <returns><list type="table"> + /// <listheader> + /// <term>ScalarSourceType</term> + /// <description>Return value</description> + /// </listheader> + /// <item> + /// <term>DataReader</term> + /// <description>The first column of the first row in the resultset. + /// </description> + /// </item> + /// <item> + /// <term>OutputParameter</term> + /// <description>The value of the first output or input/output + /// parameter returned.</description> + /// </item> + /// <item> + /// <term>ReturnValue</term> + /// <description>The value of the "return value" parameter returned. + /// </description> + /// </item> + /// <item> + /// <term>AffectedRows</term> + /// <description>The number of rows affected.</description> + /// </item> + /// </list> + /// </returns> + /// <seealso cref="ExecuteScalar(ScalarSourceType, NameOrIndexParameter)"/> + public object ExecuteScalar(ScalarSourceType sourceType) + { + return ExecuteScalar(sourceType, new NameOrIndexParameter()); + } + + /// <summary> + /// Executes the query, and returns the value with specified scalar + /// source type. + /// </summary> + /// <param name="sourceType">The method used to return the scalar value.</param> + /// <param name="nameOrIndex">The column name/index or output parameter name/index.</param> + /// <returns><list type="table"> + /// <listheader> + /// <term>ScalarSourceType</term> + /// <description>Return value</description> + /// </listheader> + /// <item> + /// <term>DataReader</term> + /// <description>The column with specified name or at specified index + /// of the first row in the resultset.</description> + /// </item> + /// <item> + /// <term>OutputParameter</term> + /// <description>The value of the output or input/output parameter + /// returned with specified name or at specified index.</description> + /// </item> + /// <item> + /// <term>ReturnValue</term> + /// <description>The value of the "return value" parameter returned. + /// The index parameter is ignored.</description> + /// </item> + /// <item> + /// <term>AffectedRows</term> + /// <description>The number of rows affected. The index parameter is + /// ignored.</description> + /// </item> + /// </list> + /// </returns> + public object ExecuteScalar(ScalarSourceType sourceType, NameOrIndexParameter nameOrIndex) + { + if (_prepared) + InitParameters(CommandAction.Select); + + switch (sourceType) + { + case ScalarSourceType.DataReader: + using (var reader = ExecuteReaderInternal()) + if (reader.Read()) + return reader.GetValue(nameOrIndex.ByName ? reader.GetOrdinal(nameOrIndex.Name) : nameOrIndex.Index); + + break; + + case ScalarSourceType.OutputParameter: + ExecuteNonQueryInternal(); + + if (nameOrIndex.ByName) + { + var name = (string)_dataProvider.Convert(nameOrIndex.Name, GetConvertTypeToParameter()); + return Parameter(name).Value; + } + + var index = nameOrIndex.Index; + foreach (IDataParameter p in SelectCommand.Parameters) + { + // Skip the return value parameter. + // + if (p.Direction == ParameterDirection.ReturnValue) + continue; + + if (0 == index) + return p.Value; + + --index; + } + break; + + case ScalarSourceType.ReturnValue: + ExecuteNonQueryInternal(); + + foreach (IDataParameter p in SelectCommand.Parameters) + if (p.Direction == ParameterDirection.ReturnValue) + return p.Value; + + break; + + case ScalarSourceType.AffectedRows: + return ExecuteNonQueryInternal(); + + default: + throw new InvalidEnumArgumentException("sourceType", + (int)sourceType, typeof(ScalarSourceType)); + } + + return null; + } + + /// <summary> + /// Executes the query, and returns the first column of the first row + /// in the resultset returned by the query. Extra columns or rows are + /// ignored. + /// </summary> + /// <returns> + /// The first column of the first row in the resultset.</returns> + /// <seealso cref="ExecuteScalar{T}(ScalarSourceType, NameOrIndexParameter)"/> + public T ExecuteScalar<T>() + { + var value = _mappingSchema.ConvertChangeType(ExecuteScalar(), typeof(T)); + return value == null && typeof(T).IsEnum ? default(T) : (T)value; + } + + /// <summary> + /// Executes the query, and returns the value with specified scalar + /// source type. + /// </summary> + /// <param name="sourceType">The method used to return the scalar + /// value.</param> + /// <returns><list type="table"> + /// <listheader> + /// <term>ScalarSourceType</term> + /// <description>Return value</description> + /// </listheader> + /// <item> + /// <term>DataReader</term> + /// <description>The first column of the first row in the resultset. + /// </description> + /// </item> + /// <item> + /// <term>OutputParameter</term> + /// <description>The value of the first output or input/output + /// parameter returned.</description> + /// </item> + /// <item> + /// <term>ReturnValue</term> + /// <description>The value of the "return value" parameter returned. + /// </description> + /// </item> + /// <item> + /// <term>AffectedRows</term> + /// <description>The number of rows affected.</description> + /// </item> + /// </list> + /// </returns> + /// <seealso cref="ExecuteScalar{T}(ScalarSourceType, NameOrIndexParameter)"/> + public T ExecuteScalar<T>(ScalarSourceType sourceType) + { + return ExecuteScalar<T>(sourceType, new NameOrIndexParameter()); + } + + /// <summary> + /// Executes the query, and returns the value with specified scalar + /// source type. + /// </summary> + /// <param name="sourceType">The method used to return the scalar value.</param> + /// <param name="nameOrIndex">The column name/index or output parameter name/index.</param> + /// <returns><list type="table"> + /// <listheader> + /// <term>ScalarSourceType</term> + /// <description>Return value</description> + /// </listheader> + /// <item> + /// <term>DataReader</term> + /// <description>The column with specified name or at specified index + /// of the first row in the resultset.</description> + /// </item> + /// <item> + /// <term>OutputParameter</term> + /// <description>The value of the output or input/output parameter + /// returned with specified name or at specified index.</description> + /// </item> + /// <item> + /// <term>ReturnValue</term> + /// <description>The value of the "return value" parameter returned. + /// The index parameter is ignored.</description> + /// </item> + /// <item> + /// <term>AffectedRows</term> + /// <description>The number of rows affected. The index parameter is + /// ignored.</description> + /// </item> + /// </list> + /// </returns> + public T ExecuteScalar<T>(ScalarSourceType sourceType, NameOrIndexParameter nameOrIndex) + { + return (T)_mappingSchema.ConvertChangeType(ExecuteScalar(sourceType, nameOrIndex), typeof(T)); + } + + #endregion + + #region ExecuteScalarList + + /// <summary> + /// Executes the query, and returns the array list of values of the + /// specified column of the every row in the resultset returned by the + /// query. Other columns are ignored. + /// </summary> + /// <param name="list">The array to fill in.</param> + /// <param name="nameOrIndex">The column name/index or output parameter name/index.</param> + /// <param name="type">The type of the each element.</param> + /// <returns>Array list of values of the specified column of the every + /// row in the resultset.</returns> + public IList ExecuteScalarList( + IList list, + Type type, + NameOrIndexParameter nameOrIndex) + { + if (list == null) + list = new ArrayList(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToScalarList(dr, nameOrIndex, list, type); + } + + /// <summary> + /// Executes the query, and returns the array list of values of first + /// column of the every row in the resultset returned by the query. + /// Other columns are ignored. + /// </summary> + /// <param name="list">The array to fill in.</param> + /// <param name="type">The type of the each element.</param> + /// <returns>Array list of values of first column of the every row in + /// the resultset.</returns> + public IList ExecuteScalarList(IList list, Type type) + { + if (list == null) + list = new ArrayList(); + + return ExecuteScalarList(list, type, 0); + } + + /// <summary> + /// Executes the query, and returns the array list of values of the + /// specified column of the every row in the resultset returned by the + /// query. Other columns are ignored. + /// </summary> + /// <param name="nameOrIndex">The column name/index.</param> + /// <param name="type">The type of the each element.</param> + /// <returns>Array list of values of the specified column of the every + /// row in the resultset.</returns> + public ArrayList ExecuteScalarList(Type type, NameOrIndexParameter nameOrIndex) + { + var list = new ArrayList(); + + ExecuteScalarList(list, type, nameOrIndex); + + return list; + } + + /// <summary> + /// Executes the query, and returns the array list of values of first + /// column of the every row in the resultset returned by the query. + /// Other columns are ignored. + /// </summary> + /// <param name="type">The type of the each element.</param> + /// <returns>Array list of values of first column of the every row in + /// the resultset.</returns> + public ArrayList ExecuteScalarList(Type type) + { + var list = new ArrayList(); + + ExecuteScalarList(list, type, 0); + + return list; + } + + /// <summary> + /// Executes the query, and returns the array list of values of the + /// specified column of the every row in the resultset returned by the + /// query. Other columns are ignored. + /// </summary> + /// <param name="list">The array to fill in.</param> + /// <param name="nameOrIndex">The column name/index or output parameter + /// name/index.</param> + /// <typeparam name="T">The type of the each element.</typeparam> + /// <returns>Array list of values of the specified column of the every + /// row in the resultset.</returns> + public IList<T> ExecuteScalarList<T>( + IList<T> list, + NameOrIndexParameter nameOrIndex) + { + if (list == null) + list = new List<T>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToScalarList(dr, nameOrIndex, list); + } + + /// <summary> + /// Executes the query, and returns the array list of values of first + /// column of the every row in the resultset returned by the query. + /// Other columns are ignored. + /// </summary> + /// <param name="list">The array to fill in.</param> + /// <typeparam name="T">The type of the each element.</typeparam> + /// <returns>Array list of values of first column of the every row in + /// the resultset.</returns> + public IList<T> ExecuteScalarList<T>(IList<T> list) + { + return ExecuteScalarList(list, 0); + } + + /// <summary> + /// Executes the query, and returns the array list of values of the + /// specified column of the every row in the resultset returned by the + /// query. Other columns are ignored. + /// </summary> + /// <param name="nameOrIndex">The column name/index or output parameter name/index.</param> + /// <typeparam name="T">The type of the each element.</typeparam> + /// <returns>Array list of values of the specified column of the every + /// row in the resultset.</returns> + public List<T> ExecuteScalarList<T>(NameOrIndexParameter nameOrIndex) + { + var list = new List<T>(); + + ExecuteScalarList(list, nameOrIndex); + + return list; + } + + /// <summary> + /// Executes the query, and returns the list of values of first + /// column of the every row in the resultset returned by the query. + /// Other columns are ignored. + /// </summary> + /// <typeparam name="T">The type of the each element.</typeparam> + /// <returns>Array list of values of first column of the every row in + /// the resultset.</returns> + public List<T> ExecuteScalarList<T>() + { + var list = new List<T>(); + + ExecuteScalarList(list, 0); + + return list; + } + + #endregion + + #region ExecuteScalarDictionary + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from a column specified by <paramref name="keyField"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<param name="dic">The dictionary to add values.</param> + ///<param name="keyField">The column name/index to load keys.</param> + ///<param name="keyFieldType">The key type.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<param name="valueFieldType">The value type.</param> + ///<returns>The loaded dictionary.</returns> + public IDictionary ExecuteScalarDictionary( + IDictionary dic, + NameOrIndexParameter keyField, Type keyFieldType, + NameOrIndexParameter valueField, Type valueFieldType) + { + if (dic == null) + dic = new Hashtable(); + + if (_prepared) + InitParameters(CommandAction.Select); + + //object nullValue = _mappingSchema.GetNullValue(type); + + if (keyField.ByName && keyField.Name.Length > 0 && keyField.Name[0] == '@') + keyField = keyField.Name.Substring(1); + + using (var dr = ExecuteReaderInternal()) + { + if (dr.Read()) + { + var keyIndex = keyField.ByName ? dr.GetOrdinal(keyField.Name) : keyField.Index; + var valueIndex = valueField.ByName ? dr.GetOrdinal(valueField.Name) : valueField.Index; + + do + { + var value = dr[valueIndex]; + var key = dr[keyIndex]; + + if (key == null || key.GetType() != keyFieldType) + key = key is DBNull ? null : _mappingSchema.ConvertChangeType(key, keyFieldType); + + if (value == null || value.GetType() != valueFieldType) + value = value is DBNull ? null : _mappingSchema.ConvertChangeType(value, valueFieldType); + + dic.Add(key, value); + } + while (dr.Read()); + } + } + + return dic; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from a column specified by <paramref name="keyField"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<param name="keyField">The column name/index to load keys.</param> + ///<param name="keyFieldType">The key type.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<param name="valueFieldType">The value type.</param> + ///<returns>The loaded dictionary.</returns> + public Hashtable ExecuteScalarDictionary( + NameOrIndexParameter keyField, Type keyFieldType, + NameOrIndexParameter valueField, Type valueFieldType) + { + var table = new Hashtable(); + + ExecuteScalarDictionary(table, keyField, keyFieldType, valueField, valueFieldType); + + return table; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from a column specified by <paramref name="keyField"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<typeparam name="TKey">The key type.</typeparam> + ///<typeparam name="T">The value type.</typeparam> + ///<param name="dic">The dictionary to add values.</param> + ///<param name="keyField">The column name/index to load keys.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<returns>The loaded dictionary.</returns> + public IDictionary<TKey,T> ExecuteScalarDictionary<TKey,T>( + IDictionary<TKey,T> dic, + NameOrIndexParameter keyField, + NameOrIndexParameter valueField) + { + if (dic == null) + dic = new Dictionary<TKey,T>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + //object nullValue = _mappingSchema.GetNullValue(type); + + var keyFieldType = typeof(TKey); + var valueFieldType = typeof(T); + + using (var dr = ExecuteReaderInternal()) + if (dr.Read()) + { + var keyIndex = keyField.ByName ? dr.GetOrdinal(keyField.Name) : keyField.Index; + var valueIndex = valueField.ByName ? dr.GetOrdinal(valueField.Name) : valueField.Index; + + do + { + var value = dr[valueIndex]; + var key = dr[keyIndex]; + + if (key == null || key.GetType() != keyFieldType) + key = key is DBNull ? null : _mappingSchema.ConvertChangeType(key, keyFieldType); + + if (value == null || value.GetType() != valueFieldType) + value = value is DBNull ? null : _mappingSchema.ConvertChangeType(value, valueFieldType); + + dic.Add((TKey) key, (T) value); + } while (dr.Read()); + } + + return dic; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from a column specified by <paramref name="keyField"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<typeparam name="TKey">The key type.</typeparam> + ///<typeparam name="T">The value type.</typeparam> + ///<param name="keyField">The column name/index to load keys.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<returns>The loaded dictionary.</returns> + public Dictionary<TKey,T> ExecuteScalarDictionary<TKey,T>( + NameOrIndexParameter keyField, + NameOrIndexParameter valueField) + { + var dic = new Dictionary<TKey,T>(); + + ExecuteScalarDictionary(dic, keyField, valueField); + + return dic; + } + + #endregion + + #region ExecuteScalarDictionary (Index) + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from columns specified by <paramref name="index"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<param name="dic">The dictionary to add values.</param> + ///<param name="index">The <see cref="MapIndex"/> of the key columns.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<param name="valueFieldType">The value type.</param> + ///<returns>The loaded dictionary.</returns> + public IDictionary ExecuteScalarDictionary( + IDictionary dic, + MapIndex index, + NameOrIndexParameter valueField, + Type valueFieldType) + { + if (dic == null) + dic = new Hashtable(); + + if (_prepared) + InitParameters(CommandAction.Select); + + //object nullValue = _mappingSchema.GetNullValue(type); + + using (var dr = ExecuteReaderInternal()) + if (dr.Read()) + { + var valueIndex = valueField.ByName ? dr.GetOrdinal(valueField.Name) : valueField.Index; + var keyIndex = new int[index.Fields.Length]; + + for (var i = 0; i < keyIndex.Length; i++) + keyIndex[i] = + index.Fields[i].ByName + ? dr.GetOrdinal(index.Fields[i].Name) + : index.Fields[i].Index; + + do + { + var value = dr[valueIndex]; + + if (value == null || value.GetType() != valueFieldType) + value = value is DBNull ? null : _mappingSchema.ConvertChangeType(value, valueFieldType); + + var key = new object[keyIndex.Length]; + + for (var i = 0; i < keyIndex.Length; i++) + key[i] = dr[keyIndex[i]]; + + dic.Add(new CompoundValue(key), value); + } while (dr.Read()); + } + + return dic; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from columns specified by <paramref name="index"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<param name="index">The <see cref="MapIndex"/> of the key columns.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<param name="valueFieldType">The value type.</param> + ///<returns>The loaded dictionary.</returns> + public Hashtable ExecuteScalarDictionary( + MapIndex index, NameOrIndexParameter valueField, Type valueFieldType) + { + var table = new Hashtable(); + + ExecuteScalarDictionary(table, index, valueField, valueFieldType); + + return table; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from columns specified by <paramref name="index"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<typeparam name="T">The value type.</typeparam> + ///<param name="dic">The dictionary to add values.</param> + ///<param name="index">The <see cref="MapIndex"/> of the key columns.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<returns>The loaded dictionary.</returns> + public IDictionary<CompoundValue,T> ExecuteScalarDictionary<T>( + IDictionary<CompoundValue, T> dic, MapIndex index, NameOrIndexParameter valueField) + { + if (dic == null) + dic = new Dictionary<CompoundValue, T>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + //object nullValue = _mappingSchema.GetNullValue(type); + + var valueFieldType = typeof(T); + + using (var dr = ExecuteReaderInternal()) + if (dr.Read()) + { + var valueIndex = valueField.ByName ? dr.GetOrdinal(valueField.Name) : valueField.Index; + var keyIndex = new int[index.Fields.Length]; + + for (var i = 0; i < keyIndex.Length; i++) + keyIndex[i] = index.Fields[i].ByName + ? dr.GetOrdinal(index.Fields[i].Name) + : index.Fields[i].Index; + + do + { + var value = dr[valueIndex]; + + if (value == null || value.GetType() != valueFieldType) + value = value is DBNull ? null : _mappingSchema.ConvertChangeType(value, valueFieldType); + + var key = new object[keyIndex.Length]; + + for (var i = 0; i < keyIndex.Length; i++) + key[i] = dr[keyIndex[i]]; + + dic.Add(new CompoundValue(key), (T) value); + } while (dr.Read()); + } + + return dic; + } + + ///<summary> + /// Executes the query, and returns the dictionary. + /// The keys are loaded from columns specified by <paramref name="index"/> and + /// values are loaded from a column specified by <paramref name="valueField"/>. + /// Other columns are ignored. + ///</summary> + ///<typeparam name="T">The value type.</typeparam> + ///<param name="index">The <see cref="MapIndex"/> of the key columns.</param> + ///<param name="valueField">The column name/index to load values.</param> + ///<returns>The loaded dictionary.</returns> + public Dictionary<CompoundValue,T> ExecuteScalarDictionary<T>( + MapIndex index, NameOrIndexParameter valueField) + { + var dic = new Dictionary<CompoundValue,T>(); + + ExecuteScalarDictionary(dic, index, valueField); + + return dic; + } + + #endregion + + #region ExecuteReader + + /// <summary> + /// Executes the command and builds an <see cref="IDataReader"/>. + /// </summary> + /// <returns>An instance of the <see cref="IDataReader"/> class.</returns> + public IDataReader ExecuteReader() + { + if (_prepared) + InitParameters(CommandAction.Select); + + return ExecuteReaderInternal(); + } + + /// <summary> + /// Executes the command and builds an <see cref="IDataReader"/>. + /// </summary> + /// <param name="commandBehavior">One of the <see cref="CommandBehavior"/> values.</param> + /// <returns>An instance of the <see cref="IDataReader"/> class.</returns> + public IDataReader ExecuteReader(CommandBehavior commandBehavior) + { + if (_prepared) + InitParameters(CommandAction.Select); + + return ExecuteReaderInternal(commandBehavior); + } + + #endregion + + #region ExecuteDataSet + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <remarks> + /// See the <see cref="ExecuteDataSet(NameOrIndexParameter)"/> method + /// to find an example. + /// </remarks> + /// <returns>The <see cref="DataSet"/>.</returns> + public DataSet ExecuteDataSet() + { + return ExecuteDataSet(null, 0, 0, "Table"); + } + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <remarks> + /// See the <see cref="ExecuteDataSet(NameOrIndexParameter)"/> method + /// to find an example. + /// </remarks> + /// <param name="dataSet">The input <see cref="DataSet"/> object.</param> + /// <returns>The <see cref="DataSet"/>.</returns> + public DataSet ExecuteDataSet( + DataSet dataSet) + { + return ExecuteDataSet(dataSet, 0, 0, "Table"); + } + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <remarks> + /// See the <see cref="ExecuteDataSet(NameOrIndexParameter)"/> method + /// to find an example. + /// </remarks> + /// <param name="table">The name or index of the populating table.</param> + /// <returns>The <see cref="DataSet"/>.</returns> + public DataSet ExecuteDataSet( + NameOrIndexParameter table) + { + return ExecuteDataSet(null, 0, 0, table); + } + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <param name="dataSet">The <see cref="DataSet"/> object to populate.</param> + /// <param name="table">The name or index of the populating table.</param> + /// <returns>The <see cref="DataSet"/>.</returns> + public DataSet ExecuteDataSet( + DataSet dataSet, + NameOrIndexParameter table) + { + return ExecuteDataSet(dataSet, 0, 0, table); + } + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <param name="dataSet">The <see cref="DataSet"/> object to populate.</param> + /// <param name="table">The name or index of the populating table.</param> + /// <param name="startRecord">The zero-based record number to start with.</param> + /// <param name="maxRecords">The maximum number of records to retrieve.</param> + /// <returns>The <see cref="DataSet"/>.</returns> + public DataSet ExecuteDataSet( + DataSet dataSet, + int startRecord, + int maxRecords, + NameOrIndexParameter table) + { + if (_prepared) + InitParameters(CommandAction.Select); + + if (dataSet == null) + dataSet = new DataSet(); + + var da = _dataProvider.CreateDataAdapterObject(); + + ((IDbDataAdapter)da).SelectCommand = SelectCommand; + + ExecuteOperation(OperationType.Fill, delegate + { + if (table.ByName) + da.Fill(dataSet, startRecord, maxRecords, table.Name); + else + da.Fill(startRecord, maxRecords, dataSet.Tables[table.Index]); + }); + + return dataSet; + } + + #endregion + + #region ExecuteDataTable + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <returns>The <see cref="DataTable"/>.</returns> + public DataTable ExecuteDataTable() + { + return ExecuteDataTable(null); + } + + /// <summary> + /// Executes a SQL statement using the provided parameters. + /// </summary> + /// <param name="dataTable">The <see cref="DataTable"/> object to populate.</param> + /// <returns>The <see cref="DataTable"/>.</returns> + public DataTable ExecuteDataTable(DataTable dataTable) + { + if (_prepared) + InitParameters(CommandAction.Select); + + if (dataTable == null) + dataTable = new DataTable(); + + var da = _dataProvider.CreateDataAdapterObject(); + ((IDbDataAdapter)da).SelectCommand = SelectCommand; + + ExecuteOperation(OperationType.Fill, delegate { da.Fill(dataTable); }); + return dataTable; + } + + /// <summary>Adds or refreshes rows in a <see cref="System.Data.DataTable"/> + /// to match those in the data source starting at the specified record + /// and retrieving up to the specified maximum number of records. + /// </summary> + /// <param name="startRecord">The zero-based record number to start with.</param> + /// <param name="maxRecords">The maximum number of records to retrieve.</param> + /// <param name="tableList">The <see cref="System.Data.DataTable"/> objects + /// to fill from the data source.</param> + public void ExecuteDataTables( + int startRecord, + int maxRecords, + params DataTable[] tableList) + { + if (tableList == null || tableList.Length == 0) + return; + + if (_prepared) + InitParameters(CommandAction.Select); + + var da = _dataProvider.CreateDataAdapterObject(); + ((IDbDataAdapter)da).SelectCommand = SelectCommand; + + ExecuteOperation(OperationType.Fill, delegate { da.Fill(startRecord, maxRecords, tableList); }); + } + + /// <summary>Adds or refreshes rows in a <see cref="System.Data.DataTable"/> + /// to match those in the data source starting at the specified record + /// and retrieving up to the specified maximum number of records. + /// </summary> + /// <param name="tableList">The <see cref="System.Data.DataTable"/> objects + /// to fill from the data source.</param> + public void ExecuteDataTables(params DataTable[] tableList) + { + ExecuteDataTables(0, 0, tableList); + } + + #endregion + + #region ExecuteObject + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <param name="entity">An object to populate.</param> + /// <param name="type">The System.Type of the object.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>A business object.</returns> + private object ExecuteObjectInternal(object entity, Type type, object[] parameters) + { + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal(/*CommandBehavior.SingleRow*/)) // Sybase provider does not support this flag. + return ExecuteOperation(OperationType.Read, () => + { + while (dr.Read()) + return + entity == null + ? _mappingSchema.MapDataReaderToObject(dr, type, parameters) + : _mappingSchema.MapDataReaderToObject(dr, entity, parameters); + + return null; + }); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <param name="entity">An object to populate.</param> + /// <returns>A business object.</returns> + public object ExecuteObject(object entity) + { + if (null == entity) + throw new ArgumentNullException("entity"); + + return ExecuteObjectInternal(entity, entity.GetType(), null); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <param name="entity">An object to populate.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>A business object.</returns> + public object ExecuteObject(object entity, params object[] parameters) + { + if (null == entity) + throw new ArgumentNullException("entity"); + + return ExecuteObjectInternal(entity, entity.GetType(), parameters); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <param name="type">Type of an object.</param> + /// <returns>A business object.</returns> + public object ExecuteObject(Type type) + { + return ExecuteObjectInternal(null, type, null); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <param name="type">Type of an object.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>A business object.</returns> + public object ExecuteObject(Type type, params object[] parameters) + { + return ExecuteObjectInternal(null, type, parameters); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <returns>A business object.</returns> + public T ExecuteObject<T>() + { + return (T)ExecuteObjectInternal(null, typeof(T), null); + } + + /// <summary> + /// Executes a SQL statement and maps resultset to an object. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>A business object.</returns> + public T ExecuteObject<T>(params object[] parameters) + { + return (T)ExecuteObjectInternal(null, typeof(T), parameters); + } + + #endregion + + #region ExecuteList + + private IList ExecuteListInternal(IList list, Type type, params object[] parameters) + { + if (list == null) + list = new ArrayList(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + //wrap this too, because of PostgreSQL lazy query execution + return ExecuteOperation(OperationType.Fill, () => _mappingSchema.MapDataReaderToList(dr, list, type, parameters)); + } + + private void ExecuteListInternal<T>(IList<T> list, params object[] parameters) + { + if (list == null) + list = new List<T>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + ExecuteOperation(OperationType.Fill, () => _mappingSchema.MapDataReaderToList(dr, list, parameters)); + } + + /// <summary> + /// Executes the query, and returns an array of business entities using the provided parameters. + /// </summary> + /// <param name="type">Type of the business object.</param> + /// <returns>An array of business objects.</returns> + public ArrayList ExecuteList(Type type) + { + var arrayList = new ArrayList(); + + ExecuteListInternal(arrayList, type, null); + + return arrayList; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <returns>Populated list of mapped business objects.</returns> + public List<T> ExecuteList<T>() + { + var list = new List<T>(); + + ExecuteListInternal<T>(list, null); + + return list; + } + + /// <summary> + /// Executes the query, and returns an array of business entities using the provided parameters. + /// </summary> + /// <param name="type">Type of the business object.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>An array of business objects.</returns> + public ArrayList ExecuteList(Type type, params object[] parameters) + { + var arrayList = new ArrayList(); + + ExecuteListInternal(arrayList, type, parameters); + + return arrayList; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>Populated list of mapped business objects.</returns> + public List<T> ExecuteList<T>(params object[] parameters) + { + var list = new List<T>(); + + ExecuteListInternal(list, parameters); + + return list; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <param name="list">The list of mapped business objects to populate.</param> + /// <param name="type">Type of an object.</param> + /// <returns>Populated list of mapped business objects.</returns> + public IList ExecuteList(IList list, Type type) + { + return ExecuteListInternal(list, type, null); + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="list">The list of mapped business objects to populate.</param> + /// <returns>Populated list of mapped business objects.</returns> + public IList<T> ExecuteList<T>(IList<T> list) + { + ExecuteListInternal(list, null); + + return list; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <param name="list">The list of mapped business objects to populate.</param> + /// <param name="type">Type of an object.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>Populated list of mapped business objects.</returns> + public IList ExecuteList(IList list, Type type, params object[] parameters) + { + return ExecuteListInternal(list, type, parameters); + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="list">The list of mapped business objects to populate.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>Populated list of mapped business objects.</returns> + public IList<T> ExecuteList<T>(IList<T> list, params object[] parameters) + { + ExecuteListInternal(list, parameters); + + return list; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="TList">Type of a list.</typeparam> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="list">The list of mapped business objects to populate.</param> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>Populated list of mapped business objects.</returns> + public TList ExecuteList<TList,T>(TList list, params object[] parameters) + where TList : IList<T> + { + ExecuteListInternal(list, typeof(T), parameters); + + return list; + } + + /// <summary> + /// Executes the query, and returns an array of business entities. + /// </summary> + /// <typeparam name="TList">Type of a list.</typeparam> + /// <typeparam name="T">Type of an object.</typeparam> + /// <param name="parameters">Additional parameters passed to object constructor through <see cref="InitContext"/>.</param> + /// <returns>Populated list of mapped business objects.</returns> + public TList ExecuteList<TList,T>(params object[] parameters) + where TList : IList<T>, new() + { + var list = new TList(); + + ExecuteListInternal(list, typeof(T), parameters); + + return list; + } + + #endregion + + #region ExecuteDictionary + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities + /// using the provided parameters. + /// </summary> + /// <include file="Examples.xml" path='examples/db[@name="ExecuteDictionary(string,Type)"]/*' /> + /// <param name="keyField">The field name or index that is used as a key to populate <see cref="Hashtable"/>.</param> + /// <param name="keyFieldType">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="Hashtable"/> class.</returns> + public Hashtable ExecuteDictionary( + NameOrIndexParameter keyField, + Type keyFieldType, + params object[] parameters) + { + var hash = new Hashtable(); + + ExecuteDictionary(hash, keyField, keyFieldType, parameters); + + return hash; + } + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities. + /// </summary> + /// <include file="Examples.xml" path='examples/db[@name="ExecuteDictionary(Hashtable,string,Type)"]/*' /> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="keyField">The field name or index that is used as a key to populate <see cref="IDictionary"/>.</param> + /// <param name="type">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="IDictionary"/>.</returns> + public IDictionary ExecuteDictionary( + IDictionary dictionary, + NameOrIndexParameter keyField, + Type type, + params object[] parameters) + { + if (dictionary == null) + dictionary = new Hashtable(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToDictionary(dr, dictionary, keyField, type, parameters); + } + + /// <summary> + /// Executes the query, and returns a dictionary of business entities. + /// </summary> + /// <typeparam name="TKey">Key's type.</typeparam> + /// <typeparam name="TValue">Value's type.</typeparam> + /// <param name="keyField">The field name or index that is used as a key to populate the dictionary.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the dictionary.</returns> + public Dictionary<TKey, TValue> ExecuteDictionary<TKey, TValue>( + NameOrIndexParameter keyField, + params object[] parameters) + { + var dictionary = new Dictionary<TKey, TValue>(); + + ExecuteDictionary<TKey, TValue>(dictionary, keyField, typeof(TValue), parameters); + + return dictionary; + } + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities. + /// </summary> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="keyField">The field name or index that is used as a key to populate <see cref="IDictionary"/>.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="IDictionary"/>.</returns> + public IDictionary<TKey, TValue> ExecuteDictionary<TKey, TValue>( + IDictionary<TKey, TValue> dictionary, + NameOrIndexParameter keyField, + params object[] parameters) + { + return ExecuteDictionary(dictionary, keyField, typeof(TValue), parameters); + } + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities. + /// </summary> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="keyField">The field name or index that is used as a key to populate <see cref="IDictionary"/>.</param> + /// <param name="destObjectType">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="IDictionary"/>.</returns> + public IDictionary<TKey, TValue> ExecuteDictionary<TKey, TValue>( + IDictionary<TKey, TValue> dictionary, + NameOrIndexParameter keyField, + Type destObjectType, + params object[] parameters) + { + if (dictionary == null) + dictionary = new Dictionary<TKey, TValue>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToDictionary( + dr, dictionary, keyField, destObjectType, parameters); + } + + #endregion + + #region ExecuteDictionary (Index) + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities + /// using the provided parameters. + /// </summary> + /// <include file="Examples.xml" path='examples/db[@name="ExecuteDictionary(string,Type)"]/*' /> + /// <param name="index">Dictionary key fields.</param> + /// <param name="type">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="Hashtable"/> class.</returns> + public Hashtable ExecuteDictionary( + MapIndex index, + Type type, + params object[] parameters) + { + var hash = new Hashtable(); + + ExecuteDictionary(hash, index, type, parameters); + + return hash; + } + + /// <summary> + /// Executes the query, and returns the <see cref="Hashtable"/> of business entities. + /// </summary> + /// <include file="Examples.xml" path='examples/db[@name="ExecuteDictionary(Hashtable,string,Type)"]/*' /> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="index">Dictionary key fields.</param> + /// <param name="type">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the <see cref="IDictionary"/>.</returns> + public IDictionary ExecuteDictionary( + IDictionary dictionary, + MapIndex index, + Type type, + params object[] parameters) + { + if (dictionary == null) + dictionary = new Hashtable(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToDictionary(dr, dictionary, index, type, parameters); + } + + /// <summary> + /// Executes the query, and returns a dictionary of business entities. + /// </summary> + /// <typeparam name="TValue">Value's type.</typeparam> + /// <param name="index">Dictionary key fields.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the dictionary.</returns> + public Dictionary<CompoundValue, TValue> ExecuteDictionary<TValue>( + MapIndex index, + params object[] parameters) + { + var dictionary = new Dictionary<CompoundValue, TValue>(); + + ExecuteDictionary<TValue>(dictionary, index, typeof(TValue), parameters); + + return dictionary; + } + + /// <summary> + /// Executes the query, and returns a dictionary of business entities. + /// </summary> + /// <typeparam name="TValue">Value's type.</typeparam> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="index">Dictionary key fields.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the dictionary.</returns> + public IDictionary<CompoundValue, TValue> ExecuteDictionary<TValue>( + IDictionary<CompoundValue, TValue> dictionary, + MapIndex index, + params object[] parameters) + { + return ExecuteDictionary(dictionary, index, typeof(TValue), parameters); + } + + /// <summary> + /// Executes the query, and returns a dictionary of business entities. + /// </summary> + /// <typeparam name="TValue">Value's type.</typeparam> + /// <param name="dictionary">A dictionary of mapped business objects to populate.</param> + /// <param name="index">Dictionary key fields.</param> + /// <param name="destObjectType">Business object type.</param> + /// <param name="parameters">Any additional parameters passed to the constructor with <see cref="InitContext"/> parameter.</param> + /// <returns>An instance of the dictionary.</returns> + public IDictionary<CompoundValue, TValue> ExecuteDictionary<TValue>( + IDictionary<CompoundValue, TValue> dictionary, + MapIndex index, + Type destObjectType, + params object[] parameters) + { + if (dictionary == null) + dictionary = new Dictionary<CompoundValue, TValue>(); + + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + return _mappingSchema.MapDataReaderToDictionary( + dr, dictionary, index, destObjectType, parameters); + } + + #endregion + + #region ExecuteResultSet + + /// <summary> + /// Executes the query, and returns multiple results. + /// </summary> + /// <param name="resultSets">Array of <see cref="MapResultSet"/> to populate.</param> + /// <returns>The populated <see cref="MapResultSet"/>.</returns> + public MapResultSet[] ExecuteResultSet(params MapResultSet[] resultSets) + { + if (_prepared) + InitParameters(CommandAction.Select); + + using (var dr = ExecuteReaderInternal()) + _mappingSchema.MapDataReaderToResultSet(dr, resultSets); + + return resultSets; + } + + /// <summary> + /// Executes the query, and returns multiple results. + /// </summary> + /// <param name="masterType">The type of the master business object.</param> + /// <param name="nextResults">Array of <see cref="MapNextResult"/> to populate.</param> + /// <returns>The populated <see cref="MapResultSet"/>.</returns> + public MapResultSet[] ExecuteResultSet( + Type masterType, params MapNextResult[] nextResults) + { + return ExecuteResultSet(_mappingSchema.ConvertToResultSet(masterType, nextResults)); + } + + /// <summary> + /// Executes the query, and returns multiple results. + /// </summary> + /// <typeparam name="T">The type of the master business object.</typeparam> + /// <param name="nextResults">Array of <see cref="MapNextResult"/> to populate.</param> + /// <returns>The populated <see cref="MapResultSet"/>.</returns> + public MapResultSet[] ExecuteResultSet<T>(params MapNextResult[] nextResults) + { + return ExecuteResultSet(_mappingSchema.ConvertToResultSet(typeof(T), nextResults)); + } + + #endregion + + #region Update + + private DbDataAdapter CreateDataAdapter() + { + var da = _dataProvider.CreateDataAdapterObject(); + + if (_insertCommand != null) ((IDbDataAdapter)da).InsertCommand = InsertCommand; + if (_updateCommand != null) ((IDbDataAdapter)da).UpdateCommand = UpdateCommand; + if (_deleteCommand != null) ((IDbDataAdapter)da).DeleteCommand = DeleteCommand; + + return da; + } + + /// <summary> + /// Calls the corresponding INSERT, UPDATE, or DELETE statements for each inserted, updated, or + /// deleted row in the specified <see cref="DataSet"/>. + /// </summary> + /// <param name="dataSet">The <see cref="DataSet"/> used to update the data source.</param> + /// <returns>The number of rows successfully updated from the <see cref="DataSet"/>.</returns> + public int Update(DataSet dataSet) + { + return Update(dataSet, "Table"); + } + + /// <summary> + /// Calls the corresponding INSERT, UPDATE, or DELETE statements for each inserted, updated, or + /// deleted row in the <see cref="DataSet"/> with the specified <see cref="DataTable"/> name. + /// </summary> + /// <param name="dataSet">The <see cref="DataSet"/> used to update the data source.</param> + /// <param name="table">The name or index of the source table to use for table mapping.</param> + /// <returns>The number of rows successfully updated from the <see cref="DataSet"/>.</returns> + public int Update( + DataSet dataSet, + NameOrIndexParameter table) + { + if (dataSet == null) + throw new ArgumentNullException( + "dataSet", Resources.DbManager_CannotUpdateNullDataset); + + var da = CreateDataAdapter(); + + return + ExecuteOperation( + OperationType.Update, + () => + (table.ByName) + ? da.Update(dataSet, table.Name) + : da.Update(dataSet.Tables[table.Index])); + } + + /// <summary> + /// Calls the corresponding INSERT, UPDATE, or DELETE statements for + /// each inserted, updated, or deleted row in the specified + /// <see cref="DataTable"/>. + /// </summary> + /// <param name="dataTable">The name or index of the source table to + /// use for table mapping.</param> + /// <returns>The number of rows successfully updated from the + /// <see cref="DataTable"/>.</returns> + public int Update(DataTable dataTable) + { + if (dataTable == null) + throw new ArgumentNullException( + "dataTable", Resources.DbManager_CannotUpdateNullDataTable); + + return + ExecuteOperation( + OperationType.Update, + () => CreateDataAdapter().Update(dataTable)); + } + + #endregion + + #region ExecuteOperation + + private void ExecuteOperation(OperationType operationType, Action operation) + { + try + { + OnBeforeOperation(operationType); + operation(); + OnAfterOperation (operationType); + } + catch (Exception ex) + { + HandleOperationException(operationType, ex); + throw; + } + } + + private T ExecuteOperation<T>(OperationType operationType, Func<T> operation) + { + var res = default(T); + + try + { + OnBeforeOperation(operationType); + res = operation(); + OnAfterOperation (operationType); + } + catch (Exception ex) + { + if (res is IDisposable) + ((IDisposable)res).Dispose(); + + HandleOperationException(operationType, ex); + throw; + } + + return res; + } + + private void HandleOperationException(OperationType op, Exception ex) + { + var dex = new DataException(this, ex); + + if (TraceSwitch.TraceError) + WriteTraceLine(string.Format("Operation '{0}' throws exception '{1}'", op, dex), TraceSwitch.DisplayName); + + OnOperationException(op, dex); + } + + #endregion + + #region IDisposable interface + + /// <summary> + /// Releases the unmanaged resources used by the <see cref="DbManager"/> and + /// optionally releases the managed resources. + /// </summary> + /// <remarks> + /// This method is called by the public <see cref="IDisposable.Dispose()"/> method + /// and the Finalize method. + /// </remarks> + /// <param name="disposing"><b>true</b> to release both managed and unmanaged resources; <b>false</b> to release only unmanaged resources.</param> + protected override void Dispose(bool disposing) + { + if (disposing) + Close(); + + base.Dispose(disposing); + } + + #endregion + } +}