2008-09-13 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.Data / System.Data.SqlClient / SqlCommandBuilder.cs
1 //
2 // System.Data.SqlClient.SqlCommandBuilder.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //
7 // Copyright (C) Tim Coleman, 2002
8 //
9
10 //
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
20 // 
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 // 
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 //
32
33 using System;
34 using System.Collections;
35 using System.ComponentModel;
36 using System.Data;
37 using System.Data.Common;
38 using System.Text;
39
40 namespace System.Data.SqlClient
41 {
42 #if NET_2_0
43         public sealed class SqlCommandBuilder : DbCommandBuilder
44 #else
45         public sealed class SqlCommandBuilder : Component
46 #endif // NET_2_0
47         {
48                 #region Fields
49
50                 bool disposed;
51
52                 DataTable dbSchemaTable;
53                 SqlDataAdapter adapter;
54                 string quotePrefix;
55                 string quoteSuffix;
56                 string tableName;
57 #if NET_2_0
58                 string _catalogSeperator = ".";
59                 string _schemaSeperator = ".";
60                 CatalogLocation _catalogLocation = CatalogLocation.Start;
61 #endif
62         
63                 SqlCommand deleteCommand;
64                 SqlCommand insertCommand;
65                 SqlCommand updateCommand;
66
67                 // Used to construct WHERE clauses
68                 static readonly string clause1 = "({0} = 1 AND {1} IS NULL)";
69                 static readonly string clause2 = "({0} = {1})";
70
71                 private SqlRowUpdatingEventHandler rowUpdatingHandler;
72
73                 #endregion // Fields
74
75                 #region Constructors
76
77                 public SqlCommandBuilder ()
78                 {
79                         dbSchemaTable = null;
80                         adapter = null;
81 #if NET_2_0
82                         quoteSuffix = "]";
83                         quotePrefix = "[";
84 #else
85                         quoteSuffix = String.Empty;
86                         quotePrefix = String.Empty;
87 #endif
88                 }
89
90                 public SqlCommandBuilder (SqlDataAdapter adapter)
91                         : this ()
92                 {
93                         DataAdapter = adapter;
94                 }
95
96                 #endregion // Constructors
97
98                 #region Properties
99
100 #if !NET_2_0
101                 [DataSysDescription ("The DataAdapter for which to automatically generate SqlCommands")]
102 #endif
103                 [DefaultValue (null)]
104                 public new SqlDataAdapter DataAdapter {
105                         get { return adapter; }
106                         set { 
107                                 if (adapter != null)
108                                         adapter.RowUpdating -= new SqlRowUpdatingEventHandler (RowUpdatingHandler);
109
110                                 adapter = value; 
111                                 if (adapter != null)
112                                         adapter.RowUpdating += new SqlRowUpdatingEventHandler (RowUpdatingHandler);
113                         }
114                 }
115
116                 private string QuotedTableName {
117                         get { return GetQuotedString (tableName); }
118                 }
119
120                 [Browsable (false)]
121                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
122 #if !NET_2_0
123                 [DataSysDescription ("The character used in a text command as the opening quote for quoting identifiers that contain special characters.")]
124 #else
125                 [EditorBrowsable (EditorBrowsableState.Never)]
126 #endif // NET_2_0
127                 public
128 #if NET_2_0
129                 override
130 #endif // NET_2_0
131                 string QuotePrefix {
132                         get { return quotePrefix; }
133                         set {
134                                 if (dbSchemaTable != null)
135                                         throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
136                                 quotePrefix = value; 
137                         }
138                 }
139
140                 [Browsable (false)]
141                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
142 #if !NET_2_0
143                 [DataSysDescription ("The character used in a text command as the closing quote for quoting identifiers that contain special characters. ")]
144 #else
145                 [EditorBrowsable (EditorBrowsableState.Never)]
146 #endif // NET_2_0
147                 public
148 #if NET_2_0
149                 override
150 #endif // NET_2_0
151                 string QuoteSuffix {
152                         get { return quoteSuffix; }
153                         set {
154                                 if (dbSchemaTable != null)
155                                         throw new InvalidOperationException ("The QuotePrefix and QuoteSuffix properties cannot be changed once an Insert, Update, or Delete command has been generated.");
156                                 quoteSuffix = value; 
157                         }
158                 }
159
160 #if NET_2_0
161                 [EditorBrowsable (EditorBrowsableState.Never)]
162                 [Browsable (false)]
163                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
164 #if !NET_2_0
165                 [DefaultValue (".")]
166 #endif
167                 public override string CatalogSeparator {
168                         get { return _catalogSeperator; }
169                         set { if (value != null) _catalogSeperator = value; }
170                 }
171
172                 [EditorBrowsable (EditorBrowsableState.Never)]
173                 [Browsable (false)]
174                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
175 #if !NET_2_0
176                 [DefaultValue (".")]
177 #endif
178                 public override string SchemaSeparator {
179                         get { return _schemaSeperator; }
180                         set {  if (value != null) _schemaSeperator = value; }
181                 }
182
183                 [EditorBrowsable (EditorBrowsableState.Never)]
184                 [Browsable (false)]
185                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
186 #if !NET_2_0
187                 [DefaultValue (CatalogLocation.Start)]
188 #endif
189                 public override CatalogLocation CatalogLocation {
190                         get { return _catalogLocation; }
191                         set { _catalogLocation = value; }
192                 }
193
194 #endif // NET_2_0
195
196                 private SqlCommand SourceCommand {
197                         get {
198                                 if (adapter != null)
199                                         return adapter.SelectCommand;
200                                 return null;
201                         }
202                 }
203
204                 #endregion // Properties
205
206                 #region Methods
207
208                 private void BuildCache (bool closeConnection)
209                 {
210                         SqlCommand sourceCommand = SourceCommand;
211                         if (sourceCommand == null)
212                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand property needs to be initialized.");
213                         SqlConnection connection = sourceCommand.Connection;
214                         if (connection == null)
215                                 throw new InvalidOperationException ("The DataAdapter.SelectCommand.Connection property needs to be initialized.");
216                                 
217                         if (dbSchemaTable == null) {
218                                 if (connection.State == ConnectionState.Open)
219                                         closeConnection = false;        
220                                 else
221                                         connection.Open ();
222         
223                                 SqlDataReader reader = sourceCommand.ExecuteReader (CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
224                                 dbSchemaTable = reader.GetSchemaTable ();
225                                 reader.Close ();
226                                 if (closeConnection)
227                                         connection.Close ();    
228                                 BuildInformation (dbSchemaTable);
229                         }
230                 }
231                 
232                 private void BuildInformation (DataTable schemaTable)
233                 {
234                         tableName = String.Empty;
235                         foreach (DataRow schemaRow in schemaTable.Rows) {
236                                 if (schemaRow.IsNull ("BaseTableName") ||
237                                     (string) schemaRow ["BaseTableName"] == String.Empty)
238                                         continue;
239
240                                 if (tableName == String.Empty) 
241                                         tableName = (string) schemaRow ["BaseTableName"];
242                                 else if (tableName != (string) schemaRow["BaseTableName"])
243                                         throw new InvalidOperationException ("Dynamic SQL generation is not supported against multiple base tables.");
244                         }
245                         if (tableName == String.Empty)
246                                 throw new InvalidOperationException ("Dynamic SQL generation is not supported with no base table.");
247                         dbSchemaTable = schemaTable;
248                 }
249
250                 private SqlCommand CreateDeleteCommand (bool option)
251                 {
252                         // If no table was found, then we can't do an delete
253                         if (QuotedTableName == String.Empty)
254                                 return null;
255
256                         CreateNewCommand (ref deleteCommand);
257
258                         string command = String.Format ("DELETE FROM {0}", QuotedTableName);
259                         StringBuilder whereClause = new StringBuilder ();
260                         bool keyFound = false;
261                         int parmIndex = 1;
262
263                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
264                                 if ((bool)schemaRow["IsExpression"] == true)
265                                         continue;
266                                 if (!IncludedInWhereClause (schemaRow)) 
267                                         continue;
268
269                                 if (whereClause.Length > 0) 
270                                         whereClause.Append (" AND ");
271
272                                 bool isKey = (bool) schemaRow ["IsKey"];
273                                 SqlParameter parameter = null;
274
275                                 if (isKey)
276                                         keyFound = true;
277
278                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
279                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
280                                 //following the 2.0 approach
281                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
282                                 if (!isKey && allowNull) {
283                                         if (option) {
284                                                 parameter = deleteCommand.Parameters.Add (String.Format ("@{0}",
285                                                                                                          schemaRow ["BaseColumnName"]),
286                                                                                           SqlDbType.Int);
287                                         } else {
288                                                 parameter = deleteCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
289                                                                                           SqlDbType.Int);
290                                         }
291                                         String sourceColumnName = (string) schemaRow ["BaseColumnName"];
292                                         parameter.Value = 1;
293
294                                         whereClause.Append ("(");
295                                         whereClause.Append (String.Format (clause1, parameter.ParameterName, 
296                                                                         GetQuotedString (sourceColumnName)));
297                                         whereClause.Append (" OR ");
298                                 }
299
300                                 if (option) {
301                                         parameter = deleteCommand.Parameters.Add (CreateParameter (schemaRow));
302                                 } else {
303                                         parameter = deleteCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
304                                 }
305                                 parameter.SourceVersion = DataRowVersion.Original;
306
307                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
308
309                                 if (!isKey && allowNull)
310                                         whereClause.Append (")");
311                         }
312                         if (!keyFound)
313                                 throw new InvalidOperationException ("Dynamic SQL generation for the DeleteCommand is not supported against a SelectCommand that does not return any key column information.");
314
315                         // We're all done, so bring it on home
316                         string sql = String.Format ("{0} WHERE ({1})", command, whereClause.ToString ());
317                         deleteCommand.CommandText = sql;
318                         return deleteCommand;
319                 }
320
321                 private SqlCommand CreateInsertCommand (bool option)
322                 {
323                         if (QuotedTableName == String.Empty)
324                                 return null;
325
326                         CreateNewCommand (ref insertCommand);
327
328                         string command = String.Format ("INSERT INTO {0}", QuotedTableName);
329                         string sql;
330                         StringBuilder columns = new StringBuilder ();
331                         StringBuilder values = new StringBuilder ();
332
333                         int parmIndex = 1;
334                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
335                                 if (!IncludedInInsert (schemaRow))
336                                         continue;
337
338                                 if (parmIndex > 1) {
339                                         columns.Append (", ");
340                                         values.Append (", ");
341                                 }
342
343                                 SqlParameter parameter = null;
344                                 if (option) {
345                                         parameter = insertCommand.Parameters.Add (CreateParameter (schemaRow));
346                                 } else {
347                                         parameter = insertCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
348                                 }
349                                 parameter.SourceVersion = DataRowVersion.Current;
350
351                                 columns.Append (GetQuotedString (parameter.SourceColumn));
352                                 values.Append (parameter.ParameterName);
353                         }
354
355                         sql = String.Format ("{0} ({1}) VALUES ({2})", command, columns.ToString (), values.ToString ());
356                         insertCommand.CommandText = sql;
357                         return insertCommand;
358                 }
359
360                 private void CreateNewCommand (ref SqlCommand command)
361                 {
362                         SqlCommand sourceCommand = SourceCommand;
363                         if (command == null) {
364                                 command = sourceCommand.Connection.CreateCommand ();
365                                 command.CommandTimeout = sourceCommand.CommandTimeout;
366                                 command.Transaction = sourceCommand.Transaction;
367                         }
368                         command.CommandType = CommandType.Text;
369                         command.UpdatedRowSource = UpdateRowSource.None;
370                         command.Parameters.Clear ();
371                 }
372
373                 private SqlCommand CreateUpdateCommand (bool option)
374                 {
375                         // If no table was found, then we can't do an update
376                         if (QuotedTableName == String.Empty)
377                                 return null;
378
379                         CreateNewCommand (ref updateCommand);
380
381                         string command = String.Format ("UPDATE {0} SET ", QuotedTableName);
382                         StringBuilder columns = new StringBuilder ();
383                         StringBuilder whereClause = new StringBuilder ();
384                         int parmIndex = 1;
385                         bool keyFound = false;
386
387                         // First, create the X=Y list for UPDATE
388                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
389                                 if (!IncludedInUpdate (schemaRow))
390                                         continue;
391                                 if (columns.Length > 0) 
392                                         columns.Append (", ");
393
394                                 SqlParameter parameter = null;
395                                 if (option) {
396                                         parameter = updateCommand.Parameters.Add (CreateParameter (schemaRow));
397                                 } else {
398                                         parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
399                                 }
400                                 parameter.SourceVersion = DataRowVersion.Current;
401
402                                 columns.Append (String.Format ("{0} = {1}", GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
403                         }
404
405                         // Now, create the WHERE clause.  This may be optimizable, but it would be ugly to incorporate
406                         // into the loop above.  "Premature optimization is the root of all evil." -- Knuth
407                         foreach (DataRow schemaRow in dbSchemaTable.Rows) {
408                                 if ((bool)schemaRow["IsExpression"] == true)
409                                         continue;
410
411                                 if (!IncludedInWhereClause (schemaRow)) 
412                                         continue;
413
414                                 if (whereClause.Length > 0) 
415                                         whereClause.Append (" AND ");
416
417                                 bool isKey = (bool) schemaRow ["IsKey"];
418                                 SqlParameter parameter = null;
419
420
421                                 if (isKey)
422                                         keyFound = true;
423
424                                 //ms.net 1.1 generates the null check for columns even if AllowDBNull is false
425                                 //while ms.net 2.0 does not. Anyways, since both forms are logically equivalent
426                                 //following the 2.0 approach
427                                 bool allowNull = (bool) schemaRow ["AllowDBNull"];
428                                 if (!isKey && allowNull) {
429                                         if (option) {
430                                                 parameter = updateCommand.Parameters.Add (String.Format ("@{0}",
431                                                                                                          schemaRow ["BaseColumnName"]),
432                                                                                           SqlDbType.Int);
433                                         } else {
434                                                 parameter = updateCommand.Parameters.Add (String.Format ("@p{0}", parmIndex++),
435                                                                                           SqlDbType.Int);
436                                         }
437                                         parameter.Value = 1;
438                                         whereClause.Append ("(");
439                                         whereClause.Append (String.Format (clause1, parameter.ParameterName,
440                                                                         GetQuotedString ((string) schemaRow ["BaseColumnName"])));
441                                         whereClause.Append (" OR ");
442                                 }
443
444                                 if (option) {
445                                         parameter = updateCommand.Parameters.Add (CreateParameter (schemaRow));
446                                 } else {
447                                         parameter = updateCommand.Parameters.Add (CreateParameter (parmIndex++, schemaRow));
448                                 }
449                                 parameter.SourceVersion = DataRowVersion.Original;
450
451                                 whereClause.Append (String.Format (clause2, GetQuotedString (parameter.SourceColumn), parameter.ParameterName));
452
453                                 if (!isKey && allowNull)
454                                         whereClause.Append (")");
455                         }
456                         if (!keyFound)
457                                 throw new InvalidOperationException ("Dynamic SQL generation for the UpdateCommand is not supported against a SelectCommand that does not return any key column information.");
458
459                         // We're all done, so bring it on home
460                         string sql = String.Format ("{0}{1} WHERE ({2})", command, columns.ToString (), whereClause.ToString ());
461                         updateCommand.CommandText = sql;
462                         return updateCommand;
463                 }
464
465                 private SqlParameter CreateParameter (DataRow schemaRow)
466                 {
467                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
468                         string name = String.Format ("@{0}", sourceColumn);
469                         SqlDbType sqlDbType = (SqlDbType) schemaRow ["ProviderType"];
470                         int size = (int) schemaRow ["ColumnSize"];
471
472                         return new SqlParameter (name, sqlDbType, size, sourceColumn);
473                 }
474
475                 private SqlParameter CreateParameter (int parmIndex, DataRow schemaRow)
476                 {
477                         string name = String.Format ("@p{0}", parmIndex);
478                         string sourceColumn = (string) schemaRow ["BaseColumnName"];
479                         SqlDbType sqlDbType = (SqlDbType) schemaRow ["ProviderType"];
480                         int size = (int) schemaRow ["ColumnSize"];
481
482                         return new SqlParameter (name, sqlDbType, size, sourceColumn);
483                 }
484
485                 public static void DeriveParameters (SqlCommand command)
486                 {
487                         command.DeriveParameters ();
488                 }
489
490 #if NET_2_0
491                 new
492 #else
493                 protected override
494 #endif
495                 void Dispose (bool disposing)
496                 {
497                         if (!disposed) {
498                                 if (disposing) {
499                                         if (insertCommand != null)
500                                                 insertCommand.Dispose ();
501                                         if (deleteCommand != null)
502                                                 deleteCommand.Dispose ();
503                                         if (updateCommand != null)
504                                                 updateCommand.Dispose ();
505                                         if (dbSchemaTable != null)
506                                                 dbSchemaTable.Dispose ();
507                                 }
508                                 disposed = true;
509                         }
510                 }
511
512                 public 
513 #if NET_2_0
514                 new
515 #endif // NET_2_0
516                 SqlCommand GetDeleteCommand ()
517                 {
518                         BuildCache (true);
519                         if (deleteCommand == null)
520                                 return CreateDeleteCommand (false);
521                         return deleteCommand;
522                 }
523
524                 public
525 #if NET_2_0
526                 new
527 #endif // NET_2_0
528                 SqlCommand GetInsertCommand ()
529                 {
530                         BuildCache (true);
531                         if (insertCommand == null)
532                                 return CreateInsertCommand (false);
533                         return insertCommand;
534                 }
535
536                 private string GetQuotedString (string value)
537                 {
538                         if (value == String.Empty || value == null)
539                                 return value;
540                         if (quotePrefix == String.Empty && quoteSuffix == String.Empty)
541                                 return value;
542                         return String.Format ("{0}{1}{2}", quotePrefix, value, quoteSuffix);
543                 }
544
545                 public 
546 #if NET_2_0
547                 new
548 #endif // NET_2_0
549                 SqlCommand GetUpdateCommand ()
550                 {
551                         BuildCache (true);
552                         if (updateCommand == null)
553                                 return CreateUpdateCommand (false);
554                         return updateCommand;
555                 }
556
557 #if NET_2_0
558                 public new SqlCommand GetUpdateCommand (bool useColumnsForParameterNames)
559                 {
560                         BuildCache (true);
561                         if (updateCommand == null)
562                                 return CreateUpdateCommand (useColumnsForParameterNames);
563                         return updateCommand;
564                 }
565
566                 public new SqlCommand GetDeleteCommand (bool useColumnsForParameterNames)
567                 {
568                         BuildCache (true);
569                         if (deleteCommand == null)
570                                 return CreateDeleteCommand (useColumnsForParameterNames);
571                         return deleteCommand;
572                 }
573
574                 public new SqlCommand GetInsertCommand (bool useColumnsForParameterNames)
575                 {
576                         BuildCache (true);
577                         if (insertCommand == null)
578                                 return CreateInsertCommand (useColumnsForParameterNames);
579                         return insertCommand;
580                 }
581                 
582                 public override string QuoteIdentifier (string unquotedIdentifier)
583                 {
584                         return base.QuoteIdentifier (unquotedIdentifier);
585                 }
586                 
587                 public override string UnquoteIdentifier (string quotedIdentifier)
588                 {
589                         return base.UnquoteIdentifier (quotedIdentifier);
590                 }
591                 
592 #endif // NET_2_0
593
594                 private bool IncludedInInsert (DataRow schemaRow)
595                 {
596                         // If the parameter has one of these properties, then we don't include it in the insert:
597                         // AutoIncrement, Hidden, Expression, RowVersion, ReadOnly
598
599                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
600                                 return false;
601                         if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
602                                 return false;
603                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
604                                 return false;
605                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
606                                 return false;
607                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
608                                 return false;
609                         return true;
610                 }
611
612                 private bool IncludedInUpdate (DataRow schemaRow)
613                 {
614                         // If the parameter has one of these properties, then we don't include it in the insert:
615                         // AutoIncrement, Hidden, RowVersion
616
617                         if (!schemaRow.IsNull ("IsAutoIncrement") && (bool) schemaRow ["IsAutoIncrement"])
618                                 return false;
619                         if (!schemaRow.IsNull ("IsHidden") && (bool) schemaRow ["IsHidden"])
620                                 return false;
621                         if (!schemaRow.IsNull ("IsRowVersion") && (bool) schemaRow ["IsRowVersion"])
622                                 return false;
623                         if (!schemaRow.IsNull ("IsExpression") && (bool) schemaRow ["IsExpression"])
624                                 return false;
625                         if (!schemaRow.IsNull ("IsReadOnly") && (bool) schemaRow ["IsReadOnly"])
626                                 return false;
627
628                         return true;
629                 }
630
631                 private bool IncludedInWhereClause (DataRow schemaRow)
632                 {
633                         if ((bool) schemaRow ["IsLong"])
634                                 return false;
635                         return true;
636                 }
637
638 #if NET_2_0
639                 new
640 #else
641                 public
642 #endif
643                 void RefreshSchema () 
644                 {
645                         // FIXME: "Figure out what else needs to be cleaned up when we refresh."
646                         tableName = String.Empty;
647                         dbSchemaTable = null;
648                         CreateNewCommand (ref deleteCommand);
649                         CreateNewCommand (ref updateCommand);
650                         CreateNewCommand (ref insertCommand);
651                 }
652
653 #if NET_2_0
654                 protected override void ApplyParameterInfo (DbParameter parameter,
655                                                             DataRow datarow,
656                                                             StatementType statementType,
657                                                             bool whereClause)
658                 {
659                         SqlParameter sqlParam = (SqlParameter) parameter;
660                         sqlParam.Size = int.Parse (datarow ["ColumnSize"].ToString ());
661                         if (datarow ["NumericPrecision"] != DBNull.Value) {
662                                 sqlParam.Precision = byte.Parse (datarow ["NumericPrecision"].ToString ());
663                         }
664                         if (datarow ["NumericScale"] != DBNull.Value) {
665                                 sqlParam.Scale = byte.Parse (datarow ["NumericScale"].ToString ());
666                         }
667                         sqlParam.SqlDbType = (SqlDbType) datarow ["ProviderType"];
668                 }
669
670                 protected override string GetParameterName (int parameterOrdinal)
671                 {
672                         return String.Format ("@p{0}",  parameterOrdinal);
673                 }
674
675                 protected override string GetParameterName (string parameterName)
676                 {
677                         return String.Format ("@{0}", parameterName);
678                 }
679
680                 protected override string GetParameterPlaceholder (int parameterOrdinal)
681                 {
682                         return GetParameterName (parameterOrdinal);
683                 }
684 #endif // NET_2_0
685
686                 #endregion // Methods
687
688                 #region Event Handlers
689
690                 private void RowUpdatingHandler (object sender, SqlRowUpdatingEventArgs args)
691                 {
692                         if (args.Command != null)
693                                 return;
694                         try {
695                                 switch (args.StatementType) {
696                                 case StatementType.Insert:
697                                         args.Command = GetInsertCommand ();
698                                         break;
699                                 case StatementType.Update:
700                                         args.Command = GetUpdateCommand ();
701                                         break;
702                                 case StatementType.Delete:
703                                         args.Command = GetDeleteCommand ();
704                                         break;
705                                 }
706                         } catch (Exception e) {
707                                 args.Errors = e;
708                                 args.Status = UpdateStatus.ErrorsOccurred;
709                         }
710                 }
711
712 #if NET_2_0
713                 protected override void SetRowUpdatingHandler (DbDataAdapter adapter)
714                 {
715                         if (!(adapter is SqlDataAdapter)) {
716                                 throw new InvalidOperationException ("Adapter needs to be a SqlDataAdapter");
717                         }
718                         rowUpdatingHandler = new SqlRowUpdatingEventHandler (RowUpdatingHandler);
719                         ((SqlDataAdapter) adapter).RowUpdating += rowUpdatingHandler;
720                 }
721
722                 protected override DataTable GetSchemaTable (DbCommand srcCommand)
723                 {
724                         using (SqlDataReader rdr = (SqlDataReader) srcCommand.ExecuteReader ())
725                                 return rdr.GetSchemaTable ();
726                 }
727
728                 protected override DbCommand InitializeCommand (DbCommand command)
729                 {
730                         if (command == null) {
731                                 command = new SqlCommand ();
732                         } else {
733                                 command.CommandTimeout = 30;
734                                 command.Transaction = null;
735                                 command.CommandType = CommandType.Text;
736                                 command.UpdatedRowSource = UpdateRowSource.None;
737                         }
738                         return command;
739                 }
740 #endif // NET_2_0
741
742                 #endregion // Event Handlers
743         }
744 }