2007-05-29 Nagappan A <anagappan@novell.com>
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlCommand.cs
1 //
2 // System.Data.SqlClient.SqlCommand.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Daniel Morgan (danmorg@sc.rr.com)
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Diego Caravana (diego@toth.it)
9 //
10 // (C) Ximian, Inc 2002 http://www.ximian.com/
11 // (C) Daniel Morgan, 2002
12 // Copyright (C) Tim Coleman, 2002
13 //
14
15 //
16 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 //
18 // Permission is hereby granted, free of charge, to any person obtaining
19 // a copy of this software and associated documentation files (the
20 // "Software"), to deal in the Software without restriction, including
21 // without limitation the rights to use, copy, modify, merge, publish,
22 // distribute, sublicense, and/or sell copies of the Software, and to
23 // permit persons to whom the Software is furnished to do so, subject to
24 // the following conditions:
25 // 
26 // The above copyright notice and this permission notice shall be
27 // included in all copies or substantial portions of the Software.
28 // 
29 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 //
37
38 using Mono.Data.Tds;
39 using Mono.Data.Tds.Protocol;
40 using System;
41 using System.Collections;
42 using System.Collections.Specialized;
43 using System.ComponentModel;
44 using System.Data;
45 using System.Data.Common;
46 #if NET_2_0
47 using System.Data.Sql;
48 #endif
49 using System.Runtime.InteropServices;
50 using System.Text;
51 using System.Xml;
52
53 namespace System.Data.SqlClient {
54         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
55          [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
56         [DefaultEventAttribute ("RecordsAffected")]
57 #if NET_2_0
58         public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
59 #else
60         public sealed class SqlCommand : Component, IDbCommand, ICloneable
61 #endif // NET_2_0
62         {
63                 #region Fields
64
65                 bool disposed = false;
66                 int commandTimeout;
67                 bool designTimeVisible;
68                 string commandText;
69                 CommandType commandType;
70                 SqlConnection connection;
71                 SqlTransaction transaction;
72                 UpdateRowSource updatedRowSource;
73                 CommandBehavior behavior = CommandBehavior.Default;
74                 SqlParameterCollection parameters;
75                 string preparedStatement = null;
76 #if NET_2_0
77                 SqlNotificationRequest notification;
78 #endif
79                 bool notificationAutoEnlist;
80
81                 #endregion // Fields
82
83                 #region Constructors
84
85                 public SqlCommand() 
86                         : this (String.Empty, null, null)
87                 {
88                 }
89
90                 public SqlCommand (string commandText) 
91                         : this (commandText, null, null)
92                 {
93                 }
94
95                 public SqlCommand (string commandText, SqlConnection connection) 
96                         : this (commandText, connection, null)
97                 {
98                 }
99
100                 public SqlCommand (string commandText, SqlConnection connection, SqlTransaction transaction) 
101                 {
102                         this.commandText = commandText;
103                         this.connection = connection;
104                         this.transaction = transaction;
105                         this.commandType = CommandType.Text;
106                         this.updatedRowSource = UpdateRowSource.Both;
107
108                         this.designTimeVisible = false;
109                         this.commandTimeout = 30;
110                         notificationAutoEnlist = true;
111                         parameters = new SqlParameterCollection (this);
112                 }
113
114                 private SqlCommand(string commandText, SqlConnection connection, SqlTransaction transaction, CommandType commandType, UpdateRowSource updatedRowSource, bool designTimeVisible, int commandTimeout, SqlParameterCollection parameters)
115                 {
116                         this.commandText = commandText;
117                         this.connection = connection;
118                         this.transaction = transaction;
119                         this.commandType = commandType;
120                         this.updatedRowSource = updatedRowSource;
121                         this.designTimeVisible = designTimeVisible;
122                         this.commandTimeout = commandTimeout;
123                         this.parameters = new SqlParameterCollection(this);
124                         for (int i = 0;i < parameters.Count;i++)
125                               this.parameters.Add(((ICloneable)parameters[i]).Clone()); 
126                 }
127                 #endregion // Constructors
128
129                 #region Properties
130
131                 internal CommandBehavior CommandBehavior {
132                         get { return behavior; }
133                 }
134
135                 [DataCategory ("Data")]
136 #if !NET_2_0
137                 [DataSysDescription ("Command text to execute.")]
138 #endif
139                 [DefaultValue ("")]
140                 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
141                 [RefreshProperties (RefreshProperties.All)]
142                 public 
143 #if NET_2_0
144                 override 
145 #endif //NET_2_0
146                 string CommandText {
147                         get { return commandText; }
148                         set { 
149                                 if (value != commandText && preparedStatement != null)
150                                         Unprepare ();
151                                 commandText = value; 
152                         }
153                 }
154
155 #if !NET_2_0
156                 [DataSysDescription ("Time to wait for command to execute.")]
157                 [DefaultValue (30)]
158 #endif
159                 public 
160 #if NET_2_0
161                 override
162 #endif //NET_2_0
163                 int CommandTimeout {
164                         get { return commandTimeout;  }
165                         set { 
166                                 if (value < 0)
167                                         throw new ArgumentException ("The property value assigned is less than 0.");
168                                 commandTimeout = value; 
169                         }
170                 }
171
172                 [DataCategory ("Data")]
173 #if !NET_2_0
174                 [DataSysDescription ("How to interpret the CommandText.")]
175 #endif
176                 [DefaultValue (CommandType.Text)]
177                 [RefreshProperties (RefreshProperties.All)]
178                 public 
179 #if NET_2_0
180                 override 
181 #endif //NET_2_0
182                 CommandType CommandType {
183                         get { return commandType; }
184                         set { 
185                                 if (value == CommandType.TableDirect)
186 #if NET_2_0
187                                         throw new ArgumentOutOfRangeException ("CommandType.TableDirect is not supported " +
188                                                                                "by the Mono SqlClient Data Provider.");
189 #else
190                                         throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SqlClient Data Provider.");
191 #endif
192
193                                 if (!Enum.IsDefined (typeof (CommandType), value))
194 #if NET_2_0
195                                         throw new ArgumentOutOfRangeException (String.Format ("The CommandType enumeration value, {0}, is invalid",
196                                                                                               value));
197 #else
198                                         throw ExceptionHelper.InvalidEnumValueException ("CommandType", value);
199 #endif
200                                 commandType = value; 
201                         }
202                 }
203
204                 [DataCategory ("Behavior")]
205                 [DefaultValue (null)]
206 #if !NET_2_0
207                 [DataSysDescription ("Connection used by the command.")]
208 #endif
209                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]                
210                 public 
211 #if NET_2_0
212                 new
213 #endif //NET_2_0
214                 SqlConnection Connection {
215                         get { return connection; }
216                         set {
217                                 if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
218                                         throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
219                                 transaction = null;
220                                 connection = value; 
221                         }
222                 }
223
224                 [Browsable (false)]
225                 [DefaultValue (true)]
226                 [DesignOnly (true)]
227                 [EditorBrowsable (EditorBrowsableState.Never)]
228                 public 
229 #if NET_2_0
230                 override
231 #endif //NET_2_0
232                 bool DesignTimeVisible {
233                         get { return designTimeVisible; } 
234                         set { designTimeVisible = value; }
235                 }
236
237                 [DataCategory ("Data")]
238 #if !NET_2_0
239                 [DataSysDescription ("The parameters collection.")]
240 #endif
241                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
242                 public 
243 #if NET_2_0
244                 new 
245 #endif //NET_2_0
246                 SqlParameterCollection Parameters {
247                         get { return parameters; }
248                 }
249
250                 internal ITds Tds {
251                         get { return Connection.Tds; }
252                 }
253
254                 IDbConnection IDbCommand.Connection {
255                         get { return Connection; }
256                         set { 
257                                 if (!(value is SqlConnection))
258                                         throw new InvalidCastException ("The value was not a valid SqlConnection.");
259                                 Connection = (SqlConnection) value;
260                         }
261                 }
262
263                 IDataParameterCollection IDbCommand.Parameters  {
264                         get { return Parameters; }
265                 }
266
267                 IDbTransaction IDbCommand.Transaction {
268                         get { return Transaction; }
269                         set { 
270                                 if (!(value is SqlTransaction))
271                                         throw new ArgumentException ();
272                                 Transaction = (SqlTransaction) value; 
273                         }
274                 }
275
276                 [Browsable (false)]
277 #if !NET_2_0
278                 [DataSysDescription ("The transaction used by the command.")]
279 #endif
280                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
281                 public new SqlTransaction Transaction {
282                         get { return transaction; }
283                         set { transaction = value; }
284                 }       
285
286                 [DataCategory ("Behavior")]
287 #if !NET_2_0
288                 [DataSysDescription ("When used by a DataAdapter.Update, how command results are applied to the current DataRow.")]
289 #endif
290                 [DefaultValue (UpdateRowSource.Both)]
291                 public 
292 #if NET_2_0
293                 override
294 #endif // NET_2_0
295                 UpdateRowSource UpdatedRowSource        {
296                         get { return updatedRowSource; }
297                         set { 
298                                 if (!Enum.IsDefined (typeof (UpdateRowSource), value))
299 #if NET_2_0
300                                         throw new ArgumentOutOfRangeException (String.Format ("The UpdateRowSource enumeration value, {0}, is invalid",
301                                                                                               value));
302 #else
303                                         throw ExceptionHelper.InvalidEnumValueException ("UpdateRowSource", value);
304 #endif
305                                 updatedRowSource = value;
306                         }
307                 }
308
309 #if NET_2_0
310                 public SqlNotificationRequest Notification { 
311                         get { return notification; } 
312                         set { notification = value; }
313                 }
314
315                 [DefaultValue (true)]
316                 public bool NotificationAutoEnlist { 
317                         get { return notificationAutoEnlist; } 
318                         set { notificationAutoEnlist = value; } 
319                 }
320 #endif
321                 #endregion // Fields
322
323                 #region Methods
324
325                 public 
326 #if NET_2_0
327                 override
328 #endif // NET_2_0
329                 void Cancel () 
330                 {
331                         if (Connection == null || Connection.Tds == null)
332                                 return;
333                         Connection.Tds.Cancel ();
334                 }
335
336 #if NET_2_0
337                 public SqlCommand Clone ()
338                 {
339                         return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout,  parameters);
340                 }               
341 #endif // NET_2_0
342
343                 internal void CloseDataReader (bool moreResults)
344                 {
345                         Connection.DataReader = null;
346
347                         if ((behavior & CommandBehavior.CloseConnection) != 0)
348                                 Connection.Close ();
349
350                         // Reset the behavior
351                         behavior = CommandBehavior.Default;
352                         if (Tds != null)
353                                 Tds.SequentialAccess = false;
354                 }
355
356                 public new SqlParameter CreateParameter () 
357                 {
358                         return new SqlParameter ();
359                 }
360
361                 internal void DeriveParameters ()
362                 {
363                         if (commandType != CommandType.StoredProcedure)
364                                 throw new InvalidOperationException (String.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
365                         ValidateCommand ("DeriveParameters");
366
367                         SqlParameterCollection localParameters = new SqlParameterCollection (this);
368                         localParameters.Add ("@procedure_name", SqlDbType.NVarChar, commandText.Length).Value = commandText;
369
370                         string sql = "sp_procedure_params_rowset";
371
372                         Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
373
374                         SqlDataReader reader = new SqlDataReader (this);
375                         parameters.Clear ();
376                         object[] dbValues = new object[reader.FieldCount];
377
378                         while (reader.Read ()) {
379                                 reader.GetValues (dbValues);
380                                 parameters.Add (new SqlParameter (dbValues));
381                         }
382                         reader.Close ();        
383
384                 }
385
386                 private void Execute (CommandBehavior behavior, bool wantResults)
387                 {
388                         int index = 0;
389                         Connection.Tds.RecordsAffected = -1;
390                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
391                         foreach (TdsMetaParameter param in parms) {
392                                 param.Validate (index++);
393                         }
394
395                         if (preparedStatement == null) {
396                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
397                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
398
399                                 StringBuilder sql1 = new StringBuilder ();
400                                 StringBuilder sql2 = new StringBuilder ();
401
402                                 if (schemaOnly || keyInfo)
403                                         sql1.Append ("SET FMTONLY OFF;");
404                                 if (keyInfo) {
405                                         sql1.Append ("SET NO_BROWSETABLE ON;");
406                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
407                                 }
408                                 if (schemaOnly) {
409                                         sql1.Append ("SET FMTONLY ON;");
410                                         sql2.Append ("SET FMTONLY OFF;");
411                                 }
412
413                                 switch (CommandType) {
414                                 case CommandType.StoredProcedure:
415                                         if (keyInfo || schemaOnly)
416                                                 Connection.Tds.Execute (sql1.ToString ());
417                                         Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
418                                         if (keyInfo || schemaOnly)
419                                                 Connection.Tds.Execute (sql2.ToString ());
420                                         break;
421                                 case CommandType.Text:
422                                         string sql;
423                                         if (sql2.Length > 0) {
424                                                 sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
425                                         } else {
426                                                 sql = String.Format ("{0}{1}", sql1.ToString (), CommandText);
427                                         }
428                                         Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
429                                         break;
430                                 }
431                         }
432                         else 
433                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
434                 }
435
436                 public 
437 #if NET_2_0
438                 override
439 #endif // NET_2_0
440                 int ExecuteNonQuery ()
441                 {
442                         ValidateCommand ("ExecuteNonQuery");
443                         int result = 0;
444                         behavior = CommandBehavior.Default;
445
446                         try {
447                                 Execute (CommandBehavior.Default, false);
448                                 result = Connection.Tds.RecordsAffected;
449                         }
450                         catch (TdsTimeoutException e) {
451                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
452                         }
453
454                         GetOutputParameters ();
455                         return result;
456                 }
457
458                 public new SqlDataReader ExecuteReader ()
459                 {
460                         return ExecuteReader (CommandBehavior.Default);
461                 }
462
463                 public new SqlDataReader ExecuteReader (CommandBehavior behavior)
464                 {
465                         ValidateCommand ("ExecuteReader");
466                         try {
467                                 this.behavior = behavior;
468                                 if ((behavior & CommandBehavior.SequentialAccess) != 0)
469                                         Tds.SequentialAccess = true;
470                                 Execute (behavior, true);
471                                 Connection.DataReader = new SqlDataReader (this);
472                         }
473                         catch (TdsTimeoutException e) {
474                                 // if behavior is closeconnection, even if it throws exception
475                                 // the connection has to be closed.
476                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
477                                         Connection.Close ();
478                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
479                         } catch (SqlException) {
480                                 // if behavior is closeconnection, even if it throws exception
481                                 // the connection has to be closed.
482                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
483                                         Connection.Close ();
484
485                                 throw;
486                         }
487
488                         return Connection.DataReader;
489                 }
490
491                 public 
492 #if NET_2_0
493                 override
494 #endif // NET_2_0
495                 object ExecuteScalar ()
496                 {
497                         try {
498                                 object result = null;
499                                 ValidateCommand ("ExecuteScalar");
500                                 behavior = CommandBehavior.Default;
501                                 try {
502                                         Execute (CommandBehavior.Default, true);
503                                 }
504                                 catch (TdsTimeoutException e) {
505                                         throw SqlException.FromTdsInternalException ((TdsInternalException) e);
506                                 }
507
508                                 if (Connection.Tds.NextResult () && Connection.Tds.NextRow ())
509                                         result = Connection.Tds.ColumnValues[0];
510                                         
511                                 if (commandType == CommandType.StoredProcedure) {
512                                         Connection.Tds.SkipToEnd ();
513                                         GetOutputParameters ();
514                                 }
515
516                                 return result;
517                         }
518                         finally {
519                                 CloseDataReader (true);
520                         }
521                 }
522
523                 public XmlReader ExecuteXmlReader ()
524                 {
525                         ValidateCommand ("ExecuteXmlReader");
526                         behavior = CommandBehavior.Default;
527                         try { 
528                                 Execute (CommandBehavior.Default, true);
529                         }
530                         catch (TdsTimeoutException e) {
531                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
532                         }
533
534                         SqlDataReader dataReader = new SqlDataReader (this);
535                         SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
536                         XmlReader xmlReader = new XmlTextReader (textReader);
537                         return xmlReader;
538                 }
539
540                 internal void GetOutputParameters ()
541                 {
542                         IList list = Connection.Tds.OutputParameters;
543
544                         if (list != null && list.Count > 0) {
545
546                                 int index = 0;
547                                 foreach (SqlParameter parameter in parameters) {
548                                         if (parameter.Direction != ParameterDirection.Input) {
549                                                 parameter.Value = list [index];
550                                                 index += 1;
551                                         }
552                                         if (index >= list.Count)
553                                                 break;
554                                 }
555                         }
556                 }
557
558                 object ICloneable.Clone ()
559                 {
560                         return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout,  parameters);
561
562                 }
563
564                 IDbDataParameter IDbCommand.CreateParameter ()
565                 {
566                         return CreateParameter ();
567                 }
568
569                 IDataReader IDbCommand.ExecuteReader ()
570                 {
571                         return ExecuteReader ();
572                 }
573
574                 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
575                 {
576                         return ExecuteReader (behavior);
577                 }
578
579                 protected override void Dispose (bool disposing)
580                 {
581                         if (disposed) return;
582                         if (disposing) {
583                                 parameters.Clear();
584                                 transaction = null;
585                                 connection = null;
586                         }
587                         disposed = true;
588                 }
589
590                 public 
591 #if NET_2_0
592                 override
593 #endif // NET_2_0
594                 void Prepare ()
595                 {
596                         ValidateCommand ("Prepare");
597
598                         if (CommandType == CommandType.StoredProcedure)
599                                 return;
600
601                         try {
602                                 foreach (SqlParameter param in Parameters)
603                                         param.CheckIfInitialized ();
604                         }catch (Exception e) {
605                                 throw new InvalidOperationException ("SqlCommand.Prepare requires " + e.Message);
606                         }
607
608                         preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
609                 }
610
611                 public void ResetCommandTimeout ()
612                 {
613                         commandTimeout = 30;
614                 }
615
616                 private void Unprepare ()
617                 {
618                         Connection.Tds.Unprepare (preparedStatement);
619                         preparedStatement = null;
620                 }
621
622                 private void ValidateCommand (string method)
623                 {
624                         if (Connection == null)
625 #if NET_2_0
626                                 throw new NullReferenceException (String.Format ("{0} requires a Connection object to continue.", method));
627 #else
628                                 throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
629 #endif
630                         if (Connection.Transaction != null && transaction != Connection.Transaction)
631                                 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
632                         if (Connection.State != ConnectionState.Open)
633 #if NET_2_0
634                                 throw new NullReferenceException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
635 #else
636                                 throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
637 #endif
638                         if (commandText == String.Empty || commandText == null)
639                                 throw new InvalidOperationException ("The command text for this Command has not been set.");
640                         if (Connection.DataReader != null)
641                                 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
642                         if (Connection.XmlReader != null)
643                                 throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
644 #if NET_2_0
645                         if (method.StartsWith ("Begin") && !Connection.AsyncProcessing)
646                                 throw new InvalidOperationException ("This Connection object is not " + 
647                                                                      "in Asynchronous mode. Use 'Asynchronous" +
648                                                                      " Processing = true' to set it.");
649 #endif // NET_2_0
650                 }
651
652 #if NET_2_0
653                 protected override DbParameter CreateDbParameter ()
654                 {
655                         return (DbParameter) CreateParameter ();
656                 }
657
658                 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
659                 {
660                         return (DbDataReader) ExecuteReader (behavior);
661                 }
662
663                 protected override DbConnection DbConnection 
664                 {
665                         get { return (DbConnection) Connection;  }
666                         set { Connection = (SqlConnection) value; }
667                 }
668                 
669                 protected override DbParameterCollection DbParameterCollection
670                 {
671                         get { return (DbParameterCollection) Parameters; }
672                 }
673
674                 protected override DbTransaction DbTransaction 
675                 {
676                         get { return (DbTransaction) Transaction; }
677                         set { Transaction = (SqlTransaction) value; }
678                 }
679 #endif // NET_2_0
680
681                 #endregion // Methods
682
683 #if NET_2_0
684                 #region Asynchronous Methods
685
686                 internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior, 
687                                                             bool wantResults,
688                                                             AsyncCallback callback, 
689                                                             object state)
690                 {
691                         IAsyncResult ar = null;
692                         Connection.Tds.RecordsAffected = -1;
693                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
694                         if (preparedStatement == null) {
695                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
696                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
697
698                                 StringBuilder sql1 = new StringBuilder ();
699                                 StringBuilder sql2 = new StringBuilder ();
700
701                                 if (schemaOnly || keyInfo)
702                                         sql1.Append ("SET FMTONLY OFF;");
703                                 if (keyInfo) {
704                                         sql1.Append ("SET NO_BROWSETABLE ON;");
705                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
706                                 }
707                                 if (schemaOnly) {
708                                         sql1.Append ("SET FMTONLY ON;");
709                                         sql2.Append ("SET FMTONLY OFF;");
710                                 }
711
712                                 switch (CommandType) {
713                                 case CommandType.StoredProcedure:
714                                         string prolog = "";
715                                         string epilog = "";
716                                         if (keyInfo || schemaOnly)
717                                                 prolog = sql1.ToString ();
718                                         if (keyInfo || schemaOnly)
719                                                 epilog = sql2.ToString ();
720                                         Connection.Tds.BeginExecuteProcedure (prolog,
721                                                                               epilog,
722                                                                               CommandText,
723                                                                               !wantResults,
724                                                                               parms,
725                                                                               callback,
726                                                                               state);
727                                                                               
728                                         break;
729                                 case CommandType.Text:
730                                         string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
731                                         if (wantResults)
732                                                 ar = Connection.Tds.BeginExecuteQuery (sql, parms, 
733                                                                                        callback, state);
734                                         else
735                                                 ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state);
736                                         break;
737                                 }
738                         }
739                         else 
740                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
741
742                         return ar;
743
744                 }
745
746                 internal void EndExecuteInternal (IAsyncResult ar)
747                 {
748                         SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar);
749                         Connection.Tds.WaitFor (sqlResult.InternalResult);
750                         Connection.Tds.CheckAndThrowException (sqlResult.InternalResult);
751                 }
752
753                 public IAsyncResult BeginExecuteNonQuery ()
754                 {
755                         return BeginExecuteNonQuery (null, null);
756                 }
757
758                 public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object state)
759                 {
760                         ValidateCommand ("BeginExecuteNonQuery");
761                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
762                         ar.EndMethod = "EndExecuteNonQuery";
763                         ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar);
764                         return ar;
765                 }
766
767                 public int EndExecuteNonQuery (IAsyncResult ar)
768                 {
769                         ValidateAsyncResult (ar, "EndExecuteNonQuery");
770                         EndExecuteInternal (ar);
771                         
772                         int ret = Connection.Tds.RecordsAffected;
773
774                         GetOutputParameters ();
775                         ( (SqlAsyncResult) ar).Ended = true;
776                         return ret;
777                 }
778
779                 public IAsyncResult BeginExecuteReader ()
780                 {
781                         return BeginExecuteReader (null, null, CommandBehavior.Default);
782                 }
783
784                 public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
785                 {
786                         return BeginExecuteReader (null, null, behavior);
787                 }
788                 
789                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state)
790                 {
791                         return BeginExecuteReader (callback, state, CommandBehavior.Default);
792                 }
793
794                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state, CommandBehavior behavior)
795                 {
796                         ValidateCommand ("BeginExecuteReader");
797                         this.behavior = behavior;
798                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
799                         ar.EndMethod = "EndExecuteReader";
800                         IAsyncResult tdsResult = BeginExecuteInternal (behavior, true, 
801                                                                        ar.BubbleCallback, state);
802                         ar.InternalResult = tdsResult;
803                         return ar;
804                 }
805
806                 public SqlDataReader EndExecuteReader (IAsyncResult ar)
807                 {
808                         ValidateAsyncResult (ar, "EndExecuteReader");
809                         EndExecuteInternal (ar);
810                         SqlDataReader reader = null;
811                         try {
812                                 reader = new SqlDataReader (this);
813                         }
814                         catch (TdsTimeoutException e) {
815                                 // if behavior is closeconnection, even if it throws exception
816                                 // the connection has to be closed.
817                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
818                                         Connection.Close ();
819                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
820                         } catch (SqlException) {
821                                 // if behavior is closeconnection, even if it throws exception
822                                 // the connection has to be closed.
823                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
824                                         Connection.Close ();
825
826                                 throw;
827                         }
828
829                         ( (SqlAsyncResult) ar).Ended = true;
830                         return reader;
831                 }
832
833                 public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object state)
834                 {
835                         ValidateCommand ("BeginExecuteXmlReader");
836                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
837                         ar.EndMethod = "EndExecuteXmlReader";
838                         ar.InternalResult = BeginExecuteInternal (behavior, true, 
839                                                                        ar.BubbleCallback, state);
840                         return ar;
841                 }
842                 
843                 public IAsyncResult BeginExecuteXmlReader ()
844                 {
845                         return BeginExecuteXmlReader (null, null);
846                 }
847                 
848
849                 public XmlReader EndExecuteXmlReader (IAsyncResult ar)
850                 {
851                         ValidateAsyncResult (ar, "EndExecuteXmlReader");
852                         EndExecuteInternal (ar);
853                         SqlDataReader reader = new SqlDataReader (this);
854                         SqlXmlTextReader textReader = new SqlXmlTextReader (reader);
855                         XmlReader xmlReader = new XmlTextReader (textReader);
856                         ( (SqlAsyncResult) ar).Ended = true;
857                         return xmlReader;
858                 }
859
860
861                 internal void ValidateAsyncResult (IAsyncResult ar, string endMethod)
862                 {
863                         if (ar == null)
864                                 throw new ArgumentException ("result passed is null!");
865                         if (! (ar is SqlAsyncResult))
866                                 throw new ArgumentException (String.Format ("cannot test validity of types {0}",
867                                                                             ar.GetType ()
868                                                                             ));
869                         SqlAsyncResult result = (SqlAsyncResult) ar;
870                         
871                         if (result.EndMethod != endMethod)
872                                 throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " + 
873                                                                                     "Expected call to {1} but {0} is called instead.",
874                                                                                     endMethod,
875                                                                                     result.EndMethod
876                                                                                     ));
877                         if (result.Ended)
878                                 throw new InvalidOperationException (String.Format ("The method {0}  cannot be called " + 
879                                                                                     "more than once for the same AsyncResult.",
880                                                                                     endMethod));
881
882                 }
883
884                 #endregion // Asynchronous Methods
885                 public event StatementCompletedEventHandler StatementCompleted;
886 #endif // NET_2_0
887         }
888 }