using System.ComponentModel;\r
using System.Collections;\r
\r
-namespace ByteFX.Data.MySQLClient\r
+namespace ByteFX.Data.MySqlClient\r
{\r
+ /// <summary>\r
+ /// Represents a SQL statement to execute against a MySQL database. This class cannot be inherited.\r
+ /// </summary>\r
+ /// <include file='docs/MySqlCommand.xml' path='MyDocs/MyMembers[@name="Class"]/*'/>\r
#if WINDOWS\r
- [System.Drawing.ToolboxBitmap( typeof(MySQLCommand), "Designers.command.bmp")]\r
+ [System.Drawing.ToolboxBitmap( typeof(MySqlCommand), "MySqlClient.resources.command.bmp")]\r
#endif\r
- public sealed class MySQLCommand : Component, IDbCommand, ICloneable\r
+ [System.ComponentModel.DesignerCategory("Code")]\r
+ public sealed class MySqlCommand : Component, IDbCommand, ICloneable\r
{\r
- MySQLConnection m_connection;\r
- MySQLTransaction m_txn;\r
- string m_sCmdText;\r
- int m_UpdateCount;\r
- UpdateRowSource m_updatedRowSource = UpdateRowSource.Both;\r
- MySQLParameterCollection m_parameters = new MySQLParameterCollection();\r
-\r
- // Implement the default constructor here.\r
- public MySQLCommand()\r
+ MySqlConnection connection;\r
+ MySqlTransaction curTransaction;\r
+ string cmdText;\r
+ int updateCount;\r
+ UpdateRowSource updatedRowSource = UpdateRowSource.Both;\r
+ MySqlParameterCollection parameters = new MySqlParameterCollection();\r
+ private ArrayList arraySql = new ArrayList();\r
+\r
+ /// <summary>\r
+ /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+ /// </summary>\r
+ public MySqlCommand()\r
{\r
}\r
\r
- // Implement other constructors here.\r
- public MySQLCommand(string cmdText)\r
+ /// <summary>\r
+ /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+ /// </summary>\r
+ public MySqlCommand(string cmdText)\r
{\r
- m_sCmdText = cmdText;\r
+ this.cmdText = cmdText;\r
}\r
\r
- public MySQLCommand(System.ComponentModel.IContainer container)\r
+ /// <summary>\r
+ /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+ /// </summary>\r
+ public MySqlCommand(System.ComponentModel.IContainer container)\r
{\r
- /// <summary>\r
- /// Required for Windows.Forms Class Composition Designer support\r
- /// </summary>\r
+ // Required for Windows.Forms Class Composition Designer support\r
container.Add(this);\r
}\r
\r
- public MySQLCommand(string cmdText, MySQLConnection connection)\r
+ /// <summary>\r
+ /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+ /// </summary>\r
+ public MySqlCommand(string cmdText, MySqlConnection connection)\r
{\r
- m_sCmdText = cmdText;\r
- m_connection = connection;\r
+ this.cmdText = cmdText;\r
+ this.connection = connection;\r
}\r
\r
+ /// <summary>\r
+ /// Disposes of this instance of MySqlCommand\r
+ /// </summary>\r
public new void Dispose() \r
{\r
base.Dispose();\r
}\r
\r
- public MySQLCommand(string cmdText, MySQLConnection connection, MySQLTransaction txn)\r
+ /// <summary>\r
+ /// Overloaded. Initializes a new instance of the MySqlCommand class.\r
+ /// </summary>\r
+ public MySqlCommand(string cmdText, MySqlConnection connection, MySqlTransaction txn)\r
{\r
- m_sCmdText = cmdText;\r
- m_connection = connection;\r
- m_txn = txn;\r
+ this.cmdText = cmdText;\r
+ this.connection = connection;\r
+ curTransaction = txn;\r
} \r
\r
- /****\r
- * IMPLEMENT THE REQUIRED PROPERTIES.\r
- ****/\r
+ #region Properties\r
+\r
+ /// <summary>\r
+ /// Gets or sets the SQL statement to execute at the data source.\r
+ /// </summary>\r
+ [Category("Data")]\r
+ [Description("Command text to execute")]\r
+#if WINDOWS\r
+ [Editor("ByteFX.Data.Common.Design.SqlCommandTextEditor,MySqlClient.Design", typeof(System.Drawing.Design.UITypeEditor))]\r
+#endif\r
public string CommandText\r
{\r
- get { return m_sCmdText; }\r
- set { m_sCmdText = value; }\r
+ get { return cmdText; }\r
+ set { cmdText = value; }\r
}\r
\r
+ internal int UpdateCount \r
+ {\r
+ get { return updateCount; }\r
+ }\r
+\r
+ /// <summary>\r
+ /// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.\r
+ /// </summary>\r
+ [Category("Misc")]\r
+ [Description("Time to wait for command to execute")]\r
public int CommandTimeout\r
{\r
- /*\r
- * The sample does not support a command time-out. As a result,\r
- * for the get, zero is returned because zero indicates an indefinite\r
- * time-out period. For the set, throw an exception.\r
- */\r
+ // TODO: support this\r
get { return 0; }\r
set { if (value != 0) throw new NotSupportedException(); }\r
}\r
\r
+ /// <summary>\r
+ /// Gets or sets a value indicating how the CommandText property is to be interpreted. Only\r
+ /// type Text is currently supported.\r
+ /// </summary>\r
+ [Category("Data")]\r
public CommandType CommandType\r
{\r
- /*\r
- * The sample only supports CommandType.Text.\r
- */\r
get { return CommandType.Text; }\r
- set { if (value != CommandType.Text) throw new NotSupportedException(); }\r
+ set \r
+ { \r
+ if (value != CommandType.Text) \r
+ throw new NotSupportedException("This version of the MySql provider only supports Text command types"); \r
+ }\r
}\r
\r
+ /// <summary>\r
+ /// Gets or sets the MySqlConnection used by this instance of the MySqlCommand.\r
+ /// </summary>\r
+ [Category("Behavior")]\r
+ [Description("Connection used by the command")]\r
public IDbConnection Connection\r
{\r
/*\r
*/\r
get \r
{ \r
- return m_connection; \r
+ return connection; \r
}\r
set\r
{\r
* so set the transaction object to return a null reference if the connection \r
* is reset.\r
*/\r
- if (m_connection != value)\r
+ if (connection != value)\r
this.Transaction = null;\r
\r
- m_connection = (MySQLConnection)value;\r
+ connection = (MySqlConnection)value;\r
}\r
}\r
\r
- public MySQLParameterCollection Parameters\r
+ /// <summary>\r
+ /// Gets the MySqlParameterCollection.\r
+ /// </summary>\r
+ [Category("Data")]\r
+ [Description("The parameters collection")]\r
+ [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]\r
+ public MySqlParameterCollection Parameters\r
{\r
- get { return m_parameters; }\r
+ get { return parameters; }\r
}\r
\r
IDataParameterCollection IDbCommand.Parameters\r
{\r
- get { return m_parameters; }\r
+ get { return parameters; }\r
}\r
\r
+ /// <summary>\r
+ /// Gets or sets the MySqlTransaction within which the MySqlCommand executes.\r
+ /// </summary>\r
+ [Browsable(false)]\r
public IDbTransaction Transaction\r
{\r
/*\r
*/\r
get \r
{ \r
- return m_txn; \r
+ return curTransaction; \r
}\r
set \r
{ \r
- m_txn = (MySQLTransaction)value; \r
+ curTransaction = (MySqlTransaction)value; \r
}\r
}\r
\r
+ /// <summary>\r
+ /// Gets or sets how command results are applied to the DataRow when used by the Update method of the DbDataAdapter.\r
+ /// </summary>\r
+ [Category("Behavior")]\r
public UpdateRowSource UpdatedRowSource\r
{\r
get \r
{ \r
- return m_updatedRowSource; \r
+ return updatedRowSource; \r
}\r
set \r
{ \r
- m_updatedRowSource = value; \r
+ updatedRowSource = value; \r
}\r
}\r
+ #endregion\r
\r
- /****\r
- * IMPLEMENT THE REQUIRED METHODS.\r
- ****/\r
+ #region Methods\r
+ /// <summary>\r
+ /// Attempts to cancel the execution of a MySqlCommand. This operation is not supported.\r
+ /// </summary>\r
+ /// <exception cref="NotSupportedException">This operation is not supported.</exception>\r
public void Cancel()\r
{\r
- // The sample does not support canceling a command\r
- // once it has been initiated.\r
throw new NotSupportedException();\r
}\r
\r
- public IDbDataParameter CreateParameter()\r
+ /// <summary>\r
+ /// Creates a new instance of a MySqlParameter object.\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ public MySqlParameter CreateParameter()\r
+ {\r
+ return new MySqlParameter();\r
+ }\r
+\r
+ IDbDataParameter IDbCommand.CreateParameter()\r
{\r
- return new MySQLParameter();\r
+ return CreateParameter();\r
}\r
\r
- /// <summary>\r
- /// Convert the SQL command into a series of ASCII bytes streaming\r
- /// each of the parameters into the proper place\r
- /// </summary>\r
- /// <param name="sql">Source SQL command with parameter markers</param>\r
- /// <returns>Byte array with all parameters included</returns>\r
- private ArrayList ConvertSQLToBytes(string sql)\r
+ private ArrayList SplitSql(string sql)\r
{\r
- ArrayList byteArrays = new ArrayList();\r
- System.IO.MemoryStream ms = new System.IO.MemoryStream();\r
- \r
- if (sql[ sql.Length-1 ] != ';')\r
- sql += ';';\r
- byte[] bytes = System.Text.Encoding.ASCII.GetBytes(sql);\r
+ ArrayList commands = new ArrayList();\r
+ System.IO.MemoryStream ms = new System.IO.MemoryStream(sql.Length);\r
+\r
+ // first we tack on a semi-colon, if not already there, to make our\r
+ // sql processing code easier. Then we ask our encoder to give us\r
+ // the bytes for this sql string\r
+ byte[] bytes = connection.Encoding.GetBytes(sql + ";");\r
\r
byte left_byte = 0;\r
- int parm_start=-1, parm_end = -1;\r
+ bool escaped = false;\r
+ int parm_start=-1;\r
for (int x=0; x < bytes.Length; x++)\r
{\r
byte b = bytes[x];\r
+\r
// if we see a quote marker, then check to see if we are opening\r
// or closing a quote\r
- if (b == '\'' || b == '\"')\r
+ if ((b == '\'' || b == '\"') && ! escaped )\r
{\r
- if (b == left_byte)\r
- {\r
- left_byte = 0;\r
- }\r
- else\r
- {\r
- if (left_byte == 0)\r
- left_byte = b;\r
- }\r
- ms.WriteByte(b);\r
+ if (b == left_byte) left_byte = 0;\r
+ else if (left_byte == 0) left_byte = b;\r
}\r
\r
- // if we find a ; not part of a quoted string, then take the parsed portion\r
- // as a sql command and add it to the array\r
- else if (b == ';' && left_byte == 0)\r
+ else if (b == '\\') \r
{\r
- byte[] sqlBytes = ms.ToArray();\r
- byteArrays.Add( sqlBytes );\r
- ms = new System.IO.MemoryStream();\r
+ escaped = !escaped;\r
}\r
\r
// if we see the marker for a parameter, then save its position and\r
// look for the end\r
- else if (b == '@' && left_byte == 0) \r
- {\r
+ else if (b == '@' && left_byte == 0 && ! escaped && parm_start==-1) \r
parm_start = x;\r
- left_byte = b;\r
- }\r
\r
// if we see a space and we are tracking a parameter, then end the parameter and have\r
- // that parameter serialize itself to the memory streams\r
- else if ((b == ' ' || b == ',' || b == ';' || b == ')') && left_byte == '@')\r
+ // that parameter serialize itself to the memory stream\r
+ else if (parm_start > -1 && (b != '@') && (b != '$') && (b != '_') && (b != '.') && ! Char.IsLetterOrDigit((char)b))\r
{\r
- parm_end = x-1;\r
- string parm_name = sql.Substring(parm_start, parm_end-parm_start+1);\r
- MySQLParameter p = (m_parameters[parm_name] as MySQLParameter);\r
- p.SerializeToBytes(ms);\r
- ms.WriteByte(b);\r
+ string parm_name = sql.Substring(parm_start, x-parm_start); \r
\r
- if (b == ';') \r
+ if (parameters.Contains( parm_name ))\r
{\r
- byte[] sqlBytes = ms.ToArray();\r
- byteArrays.Add( sqlBytes );\r
- ms = new System.IO.MemoryStream();\r
+ MySqlParameter p = (parameters[parm_name] as MySqlParameter);\r
+ p.SerializeToBytes(ms, connection );\r
}\r
+ else\r
+ {\r
+ // otherwise assume system param. just write it out\r
+ byte[] buf = connection.Encoding.GetBytes(parm_name);\r
+ ms.Write(buf, 0, buf.Length); \r
+ }\r
+ parm_start=-1;\r
+ }\r
\r
- left_byte = 0;\r
+ // if we are not in a string and we are not escaped and we are on a semi-colon,\r
+ // then write out what we have as a command\r
+ if (left_byte == 0 && ! escaped && b == ';' && ms.Length > 0)\r
+ {\r
+ bool goodcmd = false;\r
+ byte[] byteArray = ms.ToArray();\r
+ foreach (byte cmdByte in byteArray)\r
+ if (cmdByte != ' ') { goodcmd = true; break; }\r
+\r
+ if (goodcmd)\r
+ commands.Add( byteArray );\r
+ ms.SetLength(0);\r
}\r
+ else if (parm_start == -1)\r
+ ms.WriteByte(b);\r
+\r
\r
- // we want to write out the bytes in all cases except when we are parsing out a parameter\r
- else if (left_byte != '@')\r
- ms.WriteByte( b );\r
+ // we want to write out the bytes in all cases except when we are parsing out a parameter\r
+ if (escaped && b != '\\') escaped = false;\r
}\r
\r
- // if we have any left, then add it at the end\r
- if (ms.Length > 0) \r
+ return commands;\r
+ }\r
+\r
+ private void ReadOffResultSet()\r
+ {\r
+ Driver driver = connection.InternalConnection.Driver;\r
+\r
+ // first read off the schema\r
+ Packet packet = driver.ReadPacket();\r
+ while (! packet.IsLastPacket())\r
+ packet = driver.ReadPacket();\r
+\r
+ // now read off the data\r
+ packet = driver.ReadPacket();\r
+ while (! packet.IsLastPacket())\r
+ packet = driver.ReadPacket();\r
+ }\r
+\r
+ /// <summary>\r
+ /// Internal function to execute the next command in an array of commands\r
+ /// </summary>\r
+ internal CommandResult ExecuteBatch( bool stopAtResultSet )\r
+ {\r
+ Driver driver = connection.InternalConnection.Driver;\r
+\r
+ while (arraySql.Count > 0)\r
{\r
- byte[] newbytes = ms.ToArray();\r
- byteArrays.Add( newbytes );\r
- }\r
+ byte[] sql = (byte[])arraySql[0];\r
+ arraySql.RemoveAt(0);\r
\r
-/* string s = new string('c', 0);\r
- byte[] bites = (byte[])byteArrays[0];\r
- for (int zt=0; zt < bites.Length; zt++)\r
- s += Convert.ToChar(bites[zt]);\r
- System.Windows.Forms.MessageBox.Show(s);\r
-*/\r
- return byteArrays;\r
+ CommandResult result = driver.Send( DBCmd.QUERY, sql );\r
+ \r
+ if (result.IsResultSet)\r
+ {\r
+ if (stopAtResultSet) return result;\r
+ result.Clear();\r
+ continue;\r
+ }\r
+\r
+ // at this point, we know it is a zero field count\r
+ if (updateCount == -1) updateCount = 0;\r
+ updateCount += result.RowsAffected;\r
+ }\r
+ return null;\r
}\r
\r
/// <summary>\r
- /// Executes a single non-select SQL statement. Examples of this are update,\r
- /// insert, etc.\r
+ /// Executes a SQL statement against the connection and returns the number of rows affected.\r
/// </summary>\r
/// <returns>Number of rows affected</returns>\r
public int ExecuteNonQuery()\r
{\r
- /*\r
- * ExecuteNonQuery is intended for commands that do\r
- * not return results, instead returning only the number\r
- * of records affected.\r
- */\r
-\r
// There must be a valid and open connection.\r
- if (m_connection == null || m_connection.State != ConnectionState.Open)\r
- throw new InvalidOperationException("Connection must valid and open");\r
+ if (connection == null || connection.State != ConnectionState.Open)\r
+ throw new InvalidOperationException("Connection must be valid and open");\r
\r
- ArrayList list = ConvertSQLToBytes( m_sCmdText );\r
- m_UpdateCount = 0;\r
+ // Data readers have to be closed first\r
+ if (connection.Reader != null)\r
+ throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first.");\r
\r
- // Execute the command.\r
- Driver d = m_connection.Driver;\r
- try \r
- {\r
- for (int x=0; x < list.Count; x++)\r
- {\r
- d.SendQuery( (byte[])list[x] ); \r
- if (d.LastResult == 0)\r
- m_UpdateCount += d.ReadLength();\r
- }\r
- }\r
- catch (Exception ex)\r
- {\r
- throw ex;\r
- }\r
+ // execute any commands left in the queue from before.\r
+ //ExecuteBatch(false);\r
+ \r
+ arraySql = SplitSql( cmdText );\r
+ updateCount = 0;\r
+\r
+ ExecuteBatch(false);\r
\r
- return m_UpdateCount;\r
+ return (int)updateCount;\r
}\r
\r
IDataReader IDbCommand.ExecuteReader ()\r
return ExecuteReader (behavior);\r
}\r
\r
- public MySQLDataReader ExecuteReader()\r
+ /// <summary>\r
+ /// Overloaded. Sends the CommandText to the Connection and builds a MySqlDataReader.\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ public MySqlDataReader ExecuteReader()\r
{\r
return ExecuteReader(CommandBehavior.Default);\r
}\r
\r
- public MySQLDataReader ExecuteReader(CommandBehavior behavior)\r
- {\r
- /*\r
- * ExecuteReader should retrieve results from the data source\r
- * and return a DataReader that allows the user to process \r
- * the results.\r
- */\r
\r
+ /// <summary>\r
+ /// Overloaded. Sends the CommandText to the Connection and builds a MySqlDataReader.\r
+ /// </summary>\r
+ /// <returns></returns>\r
+ public MySqlDataReader ExecuteReader(CommandBehavior behavior)\r
+ {\r
// There must be a valid and open connection.\r
- if (m_connection == null || m_connection.State != ConnectionState.Open)\r
- throw new InvalidOperationException("Connection must valid and open");\r
+ if (connection == null || connection.State != ConnectionState.Open)\r
+ throw new InvalidOperationException("Connection must be valid and open");\r
\r
- if (0 != (behavior & CommandBehavior.CloseConnection))\r
- {\r
- }\r
+ // make sure all readers on this connection are closed\r
+ if (connection.Reader != null)\r
+ throw new InvalidOperationException("There is already an open DataReader associated with this Connection which must be closed first.");\r
\r
- if (0 != (behavior & CommandBehavior.Default))\r
- {\r
- }\r
+ string sql = cmdText;\r
\r
if (0 != (behavior & CommandBehavior.KeyInfo))\r
{\r
\r
if (0 != (behavior & CommandBehavior.SingleRow))\r
{\r
+ sql = String.Format("SET SQL_SELECT_LIMIT=1;{0};SET sql_select_limit=-1;", cmdText);\r
}\r
\r
+ arraySql = SplitSql( sql );\r
\r
- /*\r
- * ExecuteReader should retrieve results from the data source\r
- * and return a DataReader that allows the user to process \r
- * the results.\r
- */\r
- ArrayList cmds = ConvertSQLToBytes( m_sCmdText );\r
- m_UpdateCount = 0;\r
-\r
- MySQLDataReader reader = new MySQLDataReader(m_connection, behavior == CommandBehavior.SequentialAccess);\r
-\r
- // Execute the command.\r
- Driver d = m_connection.Driver;\r
- try \r
- {\r
- for (int x=0; x < cmds.Count; x++)\r
- {\r
-/* string st = new string('c',0);\r
- for (int z=0; z < ((byte[])cmds[x]).Length; z++)\r
- {\r
- st += Convert.ToChar( ((byte[])cmds[x])[z] );\r
- }\r
- System.Windows.Forms.MessageBox.Show(st); */\r
-\r
-/* System.IO.FileStream fs = new System.IO.FileStream("c:\\cmd.sql", System.IO.FileMode.OpenOrCreate);\r
- byte[] bites = (byte[])(cmds[0]);\r
- fs.Write(bites, 0, bites.Length);\r
- fs.Close();\r
-*/\r
- d.SendQuery( (byte[])cmds[x] );\r
- if (d.LastResult == 0)\r
- m_UpdateCount += d.ReadLength();\r
- else\r
- reader.LoadResults();\r
- }\r
- }\r
- catch (Exception ex) \r
- {\r
- throw ex;\r
- }\r
+ updateCount = -1;\r
+ MySqlDataReader reader = new MySqlDataReader(this, behavior);\r
\r
+ // move to the first resultset\r
+ reader.NextResult();\r
+ connection.Reader = reader;\r
return reader;\r
-\r
- //TODO implement rest of command behaviors on ExecuteReader\r
- /*\r
- * The only CommandBehavior option supported by this\r
- * sample is the automatic closing of the connection\r
- * when the user is done with the reader.\r
- */\r
- // if (behavior == CommandBehavior.CloseConnection)\r
- // return new TemplateDataReader(resultset, m_connection);\r
- // else\r
- // return new TemplateDataReader(resultset);\r
}\r
\r
/// <summary>\r
- /// ExecuteScalar executes a single SQL command that will return\r
- /// a single row with a single column, or if more rows/columns are\r
- /// returned it will return the first column of the first row.\r
+ /// Executes the query, and returns the first column of the first row in the \r
+ /// result set returned by the query. Extra columns or rows are ignored.\r
/// </summary>\r
/// <returns></returns>\r
public object ExecuteScalar()\r
{\r
// There must be a valid and open connection.\r
- if (m_connection == null || m_connection.State != ConnectionState.Open)\r
- throw new InvalidOperationException("Connection must valid and open");\r
-\r
- MySQLDataReader reader = new MySQLDataReader(m_connection, false);\r
- ArrayList cmds = ConvertSQLToBytes( m_sCmdText );\r
- m_UpdateCount = 0;\r
-\r
- // Execute the command.\r
- Driver d = m_connection.Driver;\r
-\r
- try \r
- {\r
- for (int x=0; x < cmds.Count; x++)\r
- {\r
- d.SendQuery( (byte[])cmds[x] );\r
- if (d.LastResult == 0)\r
- m_UpdateCount += d.ReadLength();\r
- else\r
- reader.LoadResults();\r
- }\r
- }\r
- catch (Exception ex)\r
- {\r
- throw ex;\r
- }\r
-\r
- if (! reader.Read()) return null;\r
- return reader.GetValue(0);\r
+ if (connection == null || connection.State != ConnectionState.Open)\r
+ throw new InvalidOperationException("Connection must be valid and open");\r
+\r
+ // Data readers have to be closed first\r
+ if (connection.Reader != null)\r
+ throw new MySqlException("There is already an open DataReader associated with this Connection which must be closed first.");\r
+\r
+ arraySql = SplitSql( cmdText );\r
+\r
+ updateCount = -1;\r
+ MySqlDataReader reader = new MySqlDataReader(this, 0);\r
+ reader.NextResult();\r
+ object val = null;\r
+ if (reader.Read())\r
+ val = reader.GetValue(0);\r
+ reader.Close();\r
+ return val;\r
}\r
\r
+ /// <summary>\r
+ /// Creates a prepared version of the command on an instance of MySQL Server. This\r
+ /// is currently not supported.\r
+ /// </summary>\r
public void Prepare()\r
{\r
}\r
+ #endregion\r
\r
#region ICloneable\r
+ /// <summary>\r
+ /// Creates a clone of this MySqlCommand object. CommandText, Connection, and Transaction properties\r
+ /// are included as well as the entire parameter list.\r
+ /// </summary>\r
+ /// <returns>The cloned MySqlCommand object</returns>\r
public object Clone() \r
{\r
- MySQLCommand clone = new MySQLCommand(m_sCmdText, m_connection, m_txn);\r
- foreach (MySQLParameter p in m_parameters) \r
+ MySqlCommand clone = new MySqlCommand(cmdText, connection, curTransaction);\r
+ foreach (MySqlParameter p in parameters) \r
{\r
- clone.Parameters.Add(p.Clone());\r
+ clone.Parameters.Add((p as ICloneable).Clone());\r
}\r
return clone;\r
}\r