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