* roottypes.cs: Rename from tree.cs.
[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 using System.Runtime.InteropServices;
47 using System.Text;
48 using System.Xml;
49
50 namespace System.Data.SqlClient {
51         [DesignerAttribute ("Microsoft.VSDesigner.Data.VS.SqlCommandDesigner, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.ComponentModel.Design.IDesigner")]
52          [ToolboxItemAttribute ("System.Drawing.Design.ToolboxItem, "+ Consts.AssemblySystem_Drawing)]
53 #if NET_2_0
54         public sealed class SqlCommand : DbCommand, IDbCommand, ICloneable
55 #else
56         public sealed class SqlCommand : Component, IDbCommand, ICloneable
57 #endif // NET_2_0
58         {
59                 #region Fields
60
61                 bool disposed = false;
62                 int commandTimeout;
63                 bool designTimeVisible;
64                 string commandText;
65                 CommandType commandType;
66                 SqlConnection connection;
67                 SqlTransaction transaction;
68                 UpdateRowSource updatedRowSource;
69                 CommandBehavior behavior = CommandBehavior.Default;
70                 SqlParameterCollection parameters;
71                 string preparedStatement = null;
72
73                 #endregion // Fields
74
75                 #region Constructors
76
77                 public SqlCommand() 
78                         : this (String.Empty, null, null)
79                 {
80                 }
81
82                 public SqlCommand (string commandText) 
83                         : this (commandText, null, null)
84                 {
85                 }
86
87                 public SqlCommand (string commandText, SqlConnection connection) 
88                         : this (commandText, connection, null)
89                 {
90                 }
91
92                 public SqlCommand (string commandText, SqlConnection connection, SqlTransaction transaction) 
93                 {
94                         this.commandText = commandText;
95                         this.connection = connection;
96                         this.transaction = transaction;
97                         this.commandType = CommandType.Text;
98                         this.updatedRowSource = UpdateRowSource.Both;
99
100                         this.designTimeVisible = false;
101                         this.commandTimeout = 30;
102                         parameters = new SqlParameterCollection (this);
103                 }
104
105                 private SqlCommand(string commandText, SqlConnection connection, SqlTransaction transaction, CommandType commandType, UpdateRowSource updatedRowSource, bool designTimeVisible, int commandTimeout, SqlParameterCollection parameters)
106                 {
107                         this.commandText = commandText;
108                         this.connection = connection;
109                         this.transaction = transaction;
110                         this.commandType = commandType;
111                         this.updatedRowSource = updatedRowSource;
112                         this.designTimeVisible = designTimeVisible;
113                         this.commandTimeout = commandTimeout;
114                         this.parameters = new SqlParameterCollection(this);
115                         for (int i = 0;i < parameters.Count;i++)
116                               this.parameters.Add(((ICloneable)parameters[i]).Clone()); 
117                 }
118                 #endregion // Constructors
119
120                 #region Properties
121
122                 internal CommandBehavior CommandBehavior {
123                         get { return behavior; }
124                 }
125
126                 [DataCategory ("Data")]
127 #if !NET_2_0
128                 [DataSysDescription ("Command text to execute.")]
129 #endif
130                 [DefaultValue ("")]
131                 [EditorAttribute ("Microsoft.VSDesigner.Data.SQL.Design.SqlCommandTextEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]
132                 [RefreshProperties (RefreshProperties.All)]
133                 public 
134 #if NET_2_0
135                 override 
136 #endif //NET_2_0
137                 string CommandText {
138                         get { return commandText; }
139                         set { 
140                                 if (value != commandText && preparedStatement != null)
141                                         Unprepare ();
142                                 commandText = value; 
143                         }
144                 }
145
146 #if !NET_2_0
147                 [DataSysDescription ("Time to wait for command to execute.")]
148 #endif
149                 [DefaultValue (30)]
150                 public 
151 #if NET_2_0
152                 override
153 #endif //NET_2_0
154                 int CommandTimeout {
155                         get { return commandTimeout;  }
156                         set { 
157                                 if (value < 0)
158                                         throw new ArgumentException ("The property value assigned is less than 0.");
159                                 commandTimeout = value; 
160                         }
161                 }
162
163                 [DataCategory ("Data")]
164 #if !NET_2_0
165                 [DataSysDescription ("How to interpret the CommandText.")]
166 #endif
167                 [DefaultValue (CommandType.Text)]
168                 [RefreshProperties (RefreshProperties.All)]
169                 public 
170 #if NET_2_0
171                 override 
172 #endif //NET_2_0
173                 CommandType CommandType {
174                         get { return commandType; }
175                         set { 
176                                 if (value == CommandType.TableDirect)
177                                         throw new ArgumentException ("CommandType.TableDirect is not supported by the Mono SqlClient Data Provider.");
178
179                                 if (!Enum.IsDefined (typeof (CommandType), value))
180                                         throw ExceptionHelper.InvalidEnumValueException ("CommandType", value);
181                                 commandType = value; 
182                         }
183                 }
184
185                 [DataCategory ("Behavior")]
186                 [DefaultValue (null)]
187 #if !NET_2_0
188                 [DataSysDescription ("Connection used by the command.")]
189 #endif
190                 [EditorAttribute ("Microsoft.VSDesigner.Data.Design.DbConnectionEditor, "+ Consts.AssemblyMicrosoft_VSDesigner, "System.Drawing.Design.UITypeEditor, "+ Consts.AssemblySystem_Drawing )]                
191                 public 
192 #if NET_2_0
193                 new
194 #endif //NET_2_0
195                 SqlConnection Connection {
196                         get { return connection; }
197                         set { 
198                                 if (transaction != null && connection.Transaction != null && connection.Transaction.IsOpen)
199                                         throw new InvalidOperationException ("The Connection property was changed while a transaction was in progress.");
200                                 transaction = null;
201                                 connection = value; 
202                         }
203                 }
204
205                 [Browsable (false)]
206                 [DefaultValue (true)]
207                 [DesignOnly (true)]
208                 public 
209 #if NET_2_0
210                 override
211 #endif //NET_2_0
212                 bool DesignTimeVisible {
213                         get { return designTimeVisible; } 
214                         set { designTimeVisible = value; }
215                 }
216
217                 [DataCategory ("Data")]
218 #if !NET_2_0
219                 [DataSysDescription ("The parameters collection.")]
220 #endif
221                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
222                 public 
223 #if NET_2_0
224                 new 
225 #endif //NET_2_0
226                 SqlParameterCollection Parameters {
227                         get { return parameters; }
228                 }
229
230                 internal ITds Tds {
231                         get { return Connection.Tds; }
232                 }
233
234                 IDbConnection IDbCommand.Connection {
235                         get { return Connection; }
236                         set { 
237                                 if (!(value is SqlConnection))
238                                         throw new InvalidCastException ("The value was not a valid SqlConnection.");
239                                 Connection = (SqlConnection) value;
240                         }
241                 }
242
243                 IDataParameterCollection IDbCommand.Parameters  {
244                         get { return Parameters; }
245                 }
246
247                 IDbTransaction IDbCommand.Transaction {
248                         get { return Transaction; }
249                         set { 
250                                 if (!(value is SqlTransaction))
251                                         throw new ArgumentException ();
252                                 Transaction = (SqlTransaction) value; 
253                         }
254                 }
255
256                 [Browsable (false)]
257 #if !NET_2_0
258                 [DataSysDescription ("The transaction used by the command.")]
259 #endif
260                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
261                 public new SqlTransaction Transaction {
262                         get { return transaction; }
263                         set { transaction = value; }
264                 }       
265
266                 [DataCategory ("Behavior")]
267 #if !NET_2_0
268                 [DataSysDescription ("When used by a DataAdapter.Update, how command results are applied to the current DataRow.")]
269 #endif
270                 [DefaultValue (UpdateRowSource.Both)]
271                 public 
272 #if NET_2_0
273                 override
274 #endif // NET_2_0
275                 UpdateRowSource UpdatedRowSource        {
276                         get { return updatedRowSource; }
277                         set { 
278                                 if (!Enum.IsDefined (typeof (UpdateRowSource), value))
279                                         throw ExceptionHelper.InvalidEnumValueException ("UpdateRowSource", value);
280                                 updatedRowSource = value;
281                         }
282                 }
283
284                 #endregion // Fields
285
286                 #region Methods
287
288                 public 
289 #if NET_2_0
290                 override
291 #endif // NET_2_0
292                 void Cancel () 
293                 {
294                         if (Connection == null || Connection.Tds == null)
295                                 return;
296                         Connection.Tds.Cancel ();
297                 }
298
299                 internal void CloseDataReader (bool moreResults)
300                 {
301                         Connection.DataReader = null;
302
303                         if ((behavior & CommandBehavior.CloseConnection) != 0)
304                                 Connection.Close ();
305
306                         // Reset the behavior
307                         behavior = CommandBehavior.Default;
308                         Tds.SequentialAccess = false;
309                 }
310
311                 public new SqlParameter CreateParameter () 
312                 {
313                         return new SqlParameter ();
314                 }
315
316                 internal void DeriveParameters ()
317                 {
318                         if (commandType != CommandType.StoredProcedure)
319                                 throw new InvalidOperationException (String.Format ("SqlCommand DeriveParameters only supports CommandType.StoredProcedure, not CommandType.{0}", commandType));
320                         ValidateCommand ("DeriveParameters");
321
322                         SqlParameterCollection localParameters = new SqlParameterCollection (this);
323                         localParameters.Add ("@procedure_name", SqlDbType.NVarChar, commandText.Length).Value = commandText;
324
325                         string sql = "sp_procedure_params_rowset";
326
327                         Connection.Tds.ExecProc (sql, localParameters.MetaParameters, 0, true);
328
329                         SqlDataReader reader = new SqlDataReader (this);
330                         parameters.Clear ();
331                         object[] dbValues = new object[reader.FieldCount];
332
333                         while (reader.Read ()) {
334                                 reader.GetValues (dbValues);
335                                 parameters.Add (new SqlParameter (dbValues));
336                         }
337                         reader.Close ();        
338
339                 }
340
341                 private void Execute (CommandBehavior behavior, bool wantResults)
342                 {
343                         Connection.Tds.RecordsAffected = -1;
344                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
345                         if (preparedStatement == null) {
346                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
347                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
348
349                                 StringBuilder sql1 = new StringBuilder ();
350                                 StringBuilder sql2 = new StringBuilder ();
351
352                                 if (schemaOnly || keyInfo)
353                                         sql1.Append ("SET FMTONLY OFF;");
354                                 if (keyInfo) {
355                                         sql1.Append ("SET NO_BROWSETABLE ON;");
356                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
357                                 }
358                                 if (schemaOnly) {
359                                         sql1.Append ("SET FMTONLY ON;");
360                                         sql2.Append ("SET FMTONLY OFF;");
361                                 }
362
363                                 switch (CommandType) {
364                                 case CommandType.StoredProcedure:
365                                         if (keyInfo || schemaOnly)
366                                                 Connection.Tds.Execute (sql1.ToString ());
367                                         Connection.Tds.ExecProc (CommandText, parms, CommandTimeout, wantResults);
368                                         if (keyInfo || schemaOnly)
369                                                 Connection.Tds.Execute (sql2.ToString ());
370                                         break;
371                                 case CommandType.Text:
372                                         string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
373                                         Connection.Tds.Execute (sql, parms, CommandTimeout, wantResults);
374                                         break;
375                                 }
376                         }
377                         else 
378                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
379                 }
380
381                 public 
382 #if NET_2_0
383                 override
384 #endif // NET_2_0
385                 int ExecuteNonQuery ()
386                 {
387                         ValidateCommand ("ExecuteNonQuery");
388                         int result = 0;
389                         behavior = CommandBehavior.Default;
390
391                         try {
392                                 Execute (CommandBehavior.Default, false);
393                                 result = Connection.Tds.RecordsAffected;
394                         }
395                         catch (TdsTimeoutException e) {
396                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
397                         }
398
399                         GetOutputParameters ();
400                         return result;
401                 }
402
403                 public new SqlDataReader ExecuteReader ()
404                 {
405                         return ExecuteReader (CommandBehavior.Default);
406                 }
407
408                 public new SqlDataReader ExecuteReader (CommandBehavior behavior)
409                 {
410                         ValidateCommand ("ExecuteReader");
411                         try {
412                                 this.behavior = behavior;
413                                 if ((behavior & CommandBehavior.SequentialAccess) != 0)
414                                         Tds.SequentialAccess = true;
415                                 Execute (behavior, true);
416                                 Connection.DataReader = new SqlDataReader (this);
417                         }
418                         catch (TdsTimeoutException e) {
419                                 // if behavior is closeconnection, even if it throws exception
420                                 // the connection has to be closed.
421                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
422                                         Connection.Close ();
423                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
424                         } catch (SqlException) {
425                                 // if behavior is closeconnection, even if it throws exception
426                                 // the connection has to be closed.
427                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
428                                         Connection.Close ();
429
430                                 throw;
431                         }
432
433                         return Connection.DataReader;
434                 }
435
436                 public 
437 #if NET_2_0
438                 override
439 #endif // NET_2_0
440                 object ExecuteScalar ()
441                 {
442                         ValidateCommand ("ExecuteScalar");
443                         behavior = CommandBehavior.Default;
444                         try {
445                                 Execute (CommandBehavior.Default, true);
446                         }
447                         catch (TdsTimeoutException e) {
448                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
449                         }
450
451                         if (!Connection.Tds.NextResult () || !Connection.Tds.NextRow ())
452                                 return null;
453
454                         object result = Connection.Tds.ColumnValues [0];
455                         CloseDataReader (true);
456                         return result;
457                 }
458
459                 public XmlReader ExecuteXmlReader ()
460                 {
461                         ValidateCommand ("ExecuteXmlReader");
462                         behavior = CommandBehavior.Default;
463                         try { 
464                                 Execute (CommandBehavior.Default, true);
465                         }
466                         catch (TdsTimeoutException e) {
467                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
468                         }
469
470                         SqlDataReader dataReader = new SqlDataReader (this);
471                         SqlXmlTextReader textReader = new SqlXmlTextReader (dataReader);
472                         XmlReader xmlReader = new XmlTextReader (textReader);
473                         return xmlReader;
474                 }
475
476                 internal void GetOutputParameters ()
477                 {
478                         IList list = Connection.Tds.OutputParameters;
479
480                         if (list != null && list.Count > 0) {
481
482                                 int index = 0;
483                                 foreach (SqlParameter parameter in parameters) {
484                                         if (parameter.Direction != ParameterDirection.Input) {
485                                                 parameter.Value = list [index];
486                                                 index += 1;
487                                         }
488                                         if (index >= list.Count)
489                                                 break;
490                                 }
491                         }
492                 }
493
494                 object ICloneable.Clone ()
495                 {
496                         return new SqlCommand (commandText, connection, transaction, commandType, updatedRowSource, designTimeVisible, commandTimeout,  parameters);
497
498                 }
499
500                 IDbDataParameter IDbCommand.CreateParameter ()
501                 {
502                         return CreateParameter ();
503                 }
504
505                 IDataReader IDbCommand.ExecuteReader ()
506                 {
507                         return ExecuteReader ();
508                 }
509
510                 IDataReader IDbCommand.ExecuteReader (CommandBehavior behavior)
511                 {
512                         return ExecuteReader (behavior);
513                 }
514
515                 public 
516 #if NET_2_0
517                 override
518 #endif // NET_2_0
519                 void Prepare ()
520                 {
521                         ValidateCommand ("Prepare");
522
523                         if (CommandType == CommandType.StoredProcedure)
524                                 return;
525
526                         try {
527                                 foreach (SqlParameter param in Parameters)
528                                         param.CheckIfInitialized ();
529                         }catch (Exception e) {
530                                 throw new InvalidOperationException ("SqlCommand.Prepare requires " + e.Message);
531                         }
532
533                         preparedStatement = Connection.Tds.Prepare (CommandText, Parameters.MetaParameters);
534                 }
535
536                 public void ResetCommandTimeout ()
537                 {
538                         commandTimeout = 30;
539                 }
540
541                 private void Unprepare ()
542                 {
543                         Connection.Tds.Unprepare (preparedStatement);
544                         preparedStatement = null;
545                 }
546
547                 private void ValidateCommand (string method)
548                 {
549                         if (Connection == null)
550                                 throw new InvalidOperationException (String.Format ("{0} requires a Connection object to continue.", method));
551                         if (Connection.Transaction != null && transaction != Connection.Transaction)
552                                 throw new InvalidOperationException ("The Connection object does not have the same transaction as the command object.");
553                         if (Connection.State != ConnectionState.Open)
554                                 throw new InvalidOperationException (String.Format ("ExecuteNonQuery requires an open Connection object to continue. This connection is closed.", method));
555                         if (commandText == String.Empty || commandText == null)
556                                 throw new InvalidOperationException ("The command text for this Command has not been set.");
557                         if (Connection.DataReader != null)
558                                 throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first.");
559                         if (Connection.XmlReader != null)
560                                 throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first.");
561 #if NET_2_0
562                         if (method.StartsWith ("Begin") && !Connection.AsyncProcessing)
563                                 throw new InvalidOperationException ("This Connection object is not " + 
564                                                                      "in Asynchronous mode. Use 'Asynchronous" +
565                                                                      " Processing = true' to set it.");
566 #endif // NET_2_0
567                 }
568
569 #if NET_2_0
570                 [MonoTODO]
571                 protected override DbParameter CreateDbParameter ()
572                 {
573                         return (DbParameter) CreateParameter ();
574                 }
575
576                 [MonoTODO]
577                 protected override DbDataReader ExecuteDbDataReader (CommandBehavior behavior)
578                 {
579                         return (DbDataReader) ExecuteReader (behavior);
580                 }
581
582                 [MonoTODO]                
583                 protected override DbConnection DbConnection 
584                 {
585                         get { return (DbConnection) Connection;  }
586                         set { Connection = (SqlConnection) value; }
587                 }
588                 
589                 [MonoTODO]
590                 protected override DbParameterCollection DbParameterCollection
591                 {
592                         get { return (DbParameterCollection) Parameters; }
593                 }
594
595                 [MonoTODO]
596                 protected override DbTransaction DbTransaction 
597                 {
598                         get { return (DbTransaction) Transaction; }
599                         set { Transaction = (SqlTransaction) value; }
600                 }
601 #endif // NET_2_0
602
603                 #endregion // Methods
604
605 #if NET_2_0
606                 #region Asynchronous Methods
607
608                 internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior, 
609                                                             bool wantResults,
610                                                             AsyncCallback callback, 
611                                                             object state)
612                 {
613                         IAsyncResult ar = null;
614                         Connection.Tds.RecordsAffected = -1;
615                         TdsMetaParameterCollection parms = Parameters.MetaParameters;
616                         if (preparedStatement == null) {
617                                 bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0);
618                                 bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0);
619
620                                 StringBuilder sql1 = new StringBuilder ();
621                                 StringBuilder sql2 = new StringBuilder ();
622
623                                 if (schemaOnly || keyInfo)
624                                         sql1.Append ("SET FMTONLY OFF;");
625                                 if (keyInfo) {
626                                         sql1.Append ("SET NO_BROWSETABLE ON;");
627                                         sql2.Append ("SET NO_BROWSETABLE OFF;");
628                                 }
629                                 if (schemaOnly) {
630                                         sql1.Append ("SET FMTONLY ON;");
631                                         sql2.Append ("SET FMTONLY OFF;");
632                                 }
633
634                                 switch (CommandType) {
635                                 case CommandType.StoredProcedure:
636                                         string prolog = "";
637                                         string epilog = "";
638                                         if (keyInfo || schemaOnly)
639                                                 prolog = sql1.ToString ();
640                                         if (keyInfo || schemaOnly)
641                                                 epilog = sql2.ToString ();
642                                         Connection.Tds.BeginExecuteProcedure (prolog,
643                                                                               epilog,
644                                                                               CommandText,
645                                                                               !wantResults,
646                                                                               parms,
647                                                                               callback,
648                                                                               state);
649                                                                               
650                                         break;
651                                 case CommandType.Text:
652                                         string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ());
653                                         if (wantResults)
654                                                 ar = Connection.Tds.BeginExecuteQuery (sql, parms, 
655                                                                                        callback, state);
656                                         else
657                                                 ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state);
658                                         break;
659                                 }
660                         }
661                         else 
662                                 Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults);
663
664                         return ar;
665
666                 }
667
668                 internal void EndExecuteInternal (IAsyncResult ar)
669                 {
670                         SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar);
671                         Connection.Tds.WaitFor (sqlResult.InternalResult);
672                         Connection.Tds.CheckAndThrowException (sqlResult.InternalResult);
673                 }
674
675                 public IAsyncResult BeginExecuteNonQuery ()
676                 {
677                         return BeginExecuteNonQuery (null, null);
678                 }
679
680                 public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object state)
681                 {
682                         ValidateCommand ("BeginExecuteNonQuery");
683                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
684                         ar.EndMethod = "EndExecuteNonQuery";
685                         ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar);
686                         return ar;
687                 }
688
689                 public int EndExecuteNonQuery (IAsyncResult ar)
690                 {
691                         ValidateAsyncResult (ar, "EndExecuteNonQuery");
692                         EndExecuteInternal (ar);
693                         
694                         int ret = Connection.Tds.RecordsAffected;
695
696                         GetOutputParameters ();
697                         ( (SqlAsyncResult) ar).Ended = true;
698                         return ret;
699                 }
700
701                 public IAsyncResult BeginExecuteReader ()
702                 {
703                         return BeginExecuteReader (null, null, CommandBehavior.Default);
704                 }
705
706                 public IAsyncResult BeginExecuteReader (CommandBehavior behavior)
707                 {
708                         return BeginExecuteReader (null, null, behavior);
709                 }
710                 
711                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state)
712                 {
713                         return BeginExecuteReader (callback, state, CommandBehavior.Default);
714                 }
715
716                 public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state, CommandBehavior behavior)
717                 {
718                         ValidateCommand ("BeginExecuteReader");
719                         this.behavior = behavior;
720                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
721                         ar.EndMethod = "EndExecuteReader";
722                         IAsyncResult tdsResult = BeginExecuteInternal (behavior, true, 
723                                                                        ar.BubbleCallback, state);
724                         ar.InternalResult = tdsResult;
725                         return ar;
726                 }
727
728                 public SqlDataReader EndExecuteReader (IAsyncResult ar)
729                 {
730                         ValidateAsyncResult (ar, "EndExecuteReader");
731                         EndExecuteInternal (ar);
732                         SqlDataReader reader = null;
733                         try {
734                                 reader = new SqlDataReader (this);
735                         }
736                         catch (TdsTimeoutException e) {
737                                 // if behavior is closeconnection, even if it throws exception
738                                 // the connection has to be closed.
739                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
740                                         Connection.Close ();
741                                 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
742                         } catch (SqlException) {
743                                 // if behavior is closeconnection, even if it throws exception
744                                 // the connection has to be closed.
745                                 if ((behavior & CommandBehavior.CloseConnection) != 0)
746                                         Connection.Close ();
747
748                                 throw;
749                         }
750
751                         ( (SqlAsyncResult) ar).Ended = true;
752                         return reader;
753                 }
754
755                 public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object state)
756                 {
757                         ValidateCommand ("BeginExecuteXmlReader");
758                         SqlAsyncResult ar = new SqlAsyncResult (callback, state);
759                         ar.EndMethod = "EndExecuteXmlReader";
760                         ar.InternalResult = BeginExecuteInternal (behavior, true, 
761                                                                        ar.BubbleCallback, state);
762                         return ar;
763                 }
764
765                 public XmlReader EndExecuteXmlReader (IAsyncResult ar)
766                 {
767                         ValidateAsyncResult (ar, "EndExecuteXmlReader");
768                         EndExecuteInternal (ar);
769                         SqlDataReader reader = new SqlDataReader (this);
770                         SqlXmlTextReader textReader = new SqlXmlTextReader (reader);
771                         XmlReader xmlReader = new XmlTextReader (textReader);
772                         ( (SqlAsyncResult) ar).Ended = true;
773                         return xmlReader;
774                 }
775
776
777                 internal void ValidateAsyncResult (IAsyncResult ar, string endMethod)
778                 {
779                         if (ar == null)
780                                 throw new ArgumentException ("result passed is null!");
781                         if (! (ar is SqlAsyncResult))
782                                 throw new ArgumentException (String.Format ("cannot test validity of types {0}",
783                                                                             ar.GetType ()
784                                                                             ));
785                         SqlAsyncResult result = (SqlAsyncResult) ar;
786                         
787                         if (result.EndMethod != endMethod)
788                                 throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " + 
789                                                                                     "Expected call to {1} but {0} is called instead.",
790                                                                                     endMethod,
791                                                                                     result.EndMethod
792                                                                                     ));
793                         if (result.Ended)
794                                 throw new InvalidOperationException (String.Format ("The method {0}  cannot be called " + 
795                                                                                     "more than once for the same AsyncResult.",
796                                                                                     endMethod));
797
798                 }
799
800                 #endregion // Asynchronous Methods
801 #endif // NET_2_0
802         }
803 }