2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.Data / System.Data.Common / DbDataAdapter.cs
1 //
2 // System.Data.Common.DbDataAdapter.cs
3 //
4 // Author:
5 //   Rodrigo Moya (rodrigo@ximian.com)
6 //   Tim Coleman (tim@timcoleman.com)
7 //   Sureshkumar T <tsureshkumar@novell.com>
8 //   Veerapuram Varadhan  <vvaradhan@novell.com>
9 //
10 // (C) Ximian, Inc
11 // Copyright (C) Tim Coleman, 2002-2003
12 //
13
14 //
15 // Copyright (C) 2004, 2009 Novell, Inc (http://www.novell.com)
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Collections;
39 using System.ComponentModel;
40 using System.Data;
41 using System.Reflection;
42 using System.Runtime.InteropServices;
43
44 namespace System.Data.Common
45 {
46 #if NET_2_0
47         public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
48 #else
49         public abstract class DbDataAdapter : DataAdapter, ICloneable
50 #endif
51         {
52                 #region Fields
53
54                 public const string DefaultSourceTableName = "Table";
55                 const string DefaultSourceColumnName = "Column";
56                 CommandBehavior _behavior = CommandBehavior.Default;
57
58 #if NET_2_0
59                 IDbCommand _selectCommand;
60                 IDbCommand _updateCommand;
61                 IDbCommand _deleteCommand;
62                 IDbCommand _insertCommand;
63 #endif
64
65                 #endregion // Fields
66                 
67                 #region Constructors
68
69                 protected DbDataAdapter ()
70                 {
71                 }
72
73                 protected DbDataAdapter (DbDataAdapter adapter) : base (adapter)
74                 {
75                 }
76
77                 #endregion // Fields
78
79                 #region Properties
80
81 #if NET_2_0
82                 protected internal CommandBehavior FillCommandBehavior {
83                         get { return _behavior; }
84                         set { _behavior = value; }
85                 }
86
87                 IDbCommand IDbDataAdapter.SelectCommand {
88                         get { return _selectCommand; }
89                         set { _selectCommand = value; }
90                 }
91
92                 IDbCommand IDbDataAdapter.UpdateCommand{
93                         get { return _updateCommand; }
94                         set { _updateCommand = value; }
95                 }
96
97                 IDbCommand IDbDataAdapter.DeleteCommand{
98                         get { return _deleteCommand; }
99                         set { _deleteCommand = value; }
100                 }
101
102                 IDbCommand IDbDataAdapter.InsertCommand{
103                         get { return _insertCommand; }
104                         set { _insertCommand = value; }
105                 }
106                 
107                 [Browsable (false)]
108                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
109                 public DbCommand SelectCommand {
110                         get { return (DbCommand) _selectCommand; }
111                         set { _selectCommand = value; }
112                 }
113
114                 [Browsable (false)]
115                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
116                 public DbCommand DeleteCommand {
117                         get { return (DbCommand) _deleteCommand; }
118                         set { _deleteCommand = value; }
119                 }
120
121                 [Browsable (false)]
122                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
123                 public DbCommand InsertCommand {
124                         get { return (DbCommand)_insertCommand; }
125                         set { _insertCommand = value; }
126                 }
127
128                 [Browsable (false)]
129                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
130                 public DbCommand UpdateCommand {
131                         get { return (DbCommand)_updateCommand; }
132                         set { _updateCommand = value; }
133                 }
134
135                 [DefaultValue (1)]
136                 public virtual int UpdateBatchSize {
137                         get { return 1; }
138                         set {
139                                 if (value != 1)
140                                         throw new NotSupportedException ();
141                         }
142                 }
143 #endif
144
145                 #endregion // Properties
146                 
147                 #region Events
148
149 #if ONLY_1_0 || ONLY_1_1
150                 [DataCategory ("Fill")]
151                 [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
152                 public event FillErrorEventHandler FillError;
153
154 #endif
155                 #endregion // Events
156
157                 #region Methods
158
159 #if NET_2_0
160                 protected virtual RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command,
161                                                                              StatementType statementType,
162                                                                              DataTableMapping tableMapping)
163                 {
164                         return new RowUpdatedEventArgs (dataRow, command, statementType, tableMapping);
165                 }
166
167                 protected virtual RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command,
168                                                                                StatementType statementType,
169                                                                                DataTableMapping tableMapping)
170                 {
171                         return new RowUpdatingEventArgs (dataRow, command, statementType, tableMapping);
172                 }
173
174                 protected virtual void OnRowUpdated (RowUpdatedEventArgs value)
175                 {
176                         if (Events ["RowUpdated"] != null) {
177                                 Delegate [] rowUpdatedList = Events ["RowUpdated"].GetInvocationList ();
178                                 foreach (Delegate rowUpdated in rowUpdatedList) {
179                                         MethodInfo rowUpdatedMethod = rowUpdated.Method;
180                                         rowUpdatedMethod.Invoke (value, null);
181                                 }
182                         }
183                 }
184
185                 protected virtual void OnRowUpdating (RowUpdatingEventArgs value)
186                 {
187                         if (Events ["RowUpdating"] != null) {
188                                 Delegate [] rowUpdatingList = Events ["RowUpdating"].GetInvocationList ();
189                                 foreach (Delegate rowUpdating in rowUpdatingList) {
190                                         MethodInfo rowUpdatingMethod = rowUpdating.Method;
191                                         rowUpdatingMethod.Invoke (value, null);
192                                 }
193                         }
194                 }
195 #else
196                 protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command,
197                                                                              StatementType statementType,
198                                                                              DataTableMapping tableMapping);
199
200                 protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command,
201                                                                                StatementType statementType,
202                                                                                DataTableMapping tableMapping);
203
204                 protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
205                 protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
206 #endif
207
208                 protected override void Dispose (bool disposing)
209                 {
210                         if (disposing) {
211                                 IDbDataAdapter da = (IDbDataAdapter) this;
212                                 if (da.SelectCommand != null) {
213                                         da.SelectCommand.Dispose();
214                                         da.SelectCommand = null;
215                                 }
216                                 if (da.InsertCommand != null) {
217                                         da.InsertCommand.Dispose();
218                                         da.InsertCommand = null;
219                                 }
220                                 if (da.UpdateCommand != null) {
221                                         da.UpdateCommand.Dispose();
222                                         da.UpdateCommand = null;
223                                 }
224                                 if (da.DeleteCommand != null) {
225                                         da.DeleteCommand.Dispose();
226                                         da.DeleteCommand = null;
227                                 }
228                         }
229                 }
230
231                 public override int Fill (DataSet dataSet)
232                 {
233                         return Fill (dataSet, 0, 0, DefaultSourceTableName, ((IDbDataAdapter) this).SelectCommand, _behavior);
234                 }
235
236                 public int Fill (DataTable dataTable)
237                 {
238                         if (dataTable == null)
239                                 throw new ArgumentNullException ("DataTable");
240
241                         return Fill (dataTable, ((IDbDataAdapter) this).SelectCommand, _behavior);
242                 }
243
244                 public int Fill (DataSet dataSet, string srcTable)
245                 {
246                         return Fill (dataSet, 0, 0, srcTable, ((IDbDataAdapter) this).SelectCommand, _behavior);
247                 }
248
249 #if !NET_2_0
250                 protected virtual int Fill (DataTable dataTable, IDataReader dataReader)
251                 {
252                         return base.FillInternal (dataTable, dataReader);
253                 }
254 #endif
255
256                 protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
257                 {
258                         CommandBehavior commandBehavior = behavior;
259
260                         // first see that the connection is not close.
261                         if (command.Connection.State == ConnectionState.Closed) {
262                                 command.Connection.Open ();
263                                 commandBehavior |= CommandBehavior.CloseConnection;
264                         }
265                         return Fill (dataTable, command.ExecuteReader (commandBehavior));
266                 }
267
268                 public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable)
269                 {
270                         return this.Fill (dataSet, startRecord, maxRecords, srcTable, ((IDbDataAdapter) this).SelectCommand, _behavior);
271                 }
272
273 #if NET_2_0
274                 [MonoTODO]
275                 public int Fill (int startRecord, int maxRecords, params DataTable[] dataTables)
276                 {
277                         throw new NotImplementedException ();
278                 }
279
280                 [MonoTODO]
281                 protected virtual int Fill (DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior)
282                 {
283                         throw new NotImplementedException ();
284                 }
285 #else
286                 protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
287                 {
288                         return base.FillInternal (dataSet, srcTable, dataReader, startRecord, maxRecords);
289                 }
290 #endif
291
292                 protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
293                 {
294                         if (command.Connection == null)
295                                 throw new InvalidOperationException ("Connection state is closed");
296
297                         if (MissingSchemaAction == MissingSchemaAction.AddWithKey)
298                                 behavior |= CommandBehavior.KeyInfo;
299                         CommandBehavior commandBehavior = behavior;
300
301                         if (command.Connection.State == ConnectionState.Closed) {
302                                 command.Connection.Open ();
303                                 commandBehavior |= CommandBehavior.CloseConnection;
304                         }
305                         return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior),
306                                 startRecord, maxRecords);
307                 }
308
309 #if NET_2_0
310                 /// <summary>
311                 /// Fills the given datatable using values from reader. if a value 
312                 /// for a column is  null, that will be filled with default value. 
313                 /// </summary>
314                 /// <returns>No. of rows affected </returns>
315                 internal static int FillFromReader (DataTable table,
316                                                     IDataReader reader,
317                                                     int start,
318                                                     int length,
319                                                     int [] mapping,
320                                                     LoadOption loadOption
321                                                     )
322                 {
323                         if (reader.FieldCount == 0)
324                                 return 0 ;
325
326                         for (int i = 0; i < start; i++)
327                                 reader.Read ();
328
329                         int counter = 0;
330                         object [] values = new object [mapping.Length];
331                         while (reader.Read () && (length == 0 || counter < length)) {
332                                 for (int i = 0 ; i < mapping.Length; i++)
333                                         values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
334                                 table.BeginLoadData ();
335                                 table.LoadDataRow (values, loadOption);
336                                 table.EndLoadData ();
337                                 counter++;
338                         }
339                         return counter;
340                 }
341
342                 internal static int FillFromReader (DataTable table,
343                                                     IDataReader reader,
344                                                     int start,
345                                                     int length,
346                                                     int [] mapping,
347                                                     LoadOption loadOption,
348                                                     FillErrorEventHandler errorHandler)
349                 {
350                         if (reader.FieldCount == 0)
351                                 return 0 ;
352
353                         for (int i = 0; i < start; i++)
354                                 reader.Read ();
355
356                         int counter = 0;
357                         object [] values = new object [mapping.Length];
358                         while (reader.Read () && (length == 0 || counter < length)) {
359                                 for (int i = 0 ; i < mapping.Length; i++)
360                                         values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
361                                 table.BeginLoadData ();
362                                 try {
363                                         table.LoadDataRow (values, loadOption);
364                                 } catch (Exception e) {
365                                         FillErrorEventArgs args = new FillErrorEventArgs (table, values);
366                                         args.Errors = e;
367                                         args.Continue = false;
368                                         errorHandler (table, args);
369                                         // if args.Continue is not set to true or if a handler is not set, rethrow the error..
370                                         if(!args.Continue)
371                                                 throw e;
372                                 }
373                                 table.EndLoadData ();
374                                 counter++;
375                         }
376                         return counter;
377                 }
378 #endif // NET_2_0
379
380                 public override DataTable [] FillSchema (DataSet dataSet, SchemaType schemaType)
381                 {
382                         return FillSchema (dataSet, schemaType, ((IDbDataAdapter) this).SelectCommand, DefaultSourceTableName, _behavior);
383                 }
384
385                 public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
386                 {
387                         return FillSchema (dataTable, schemaType, ((IDbDataAdapter) this).SelectCommand, _behavior);
388                 }
389
390                 public DataTable [] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
391                 {
392                         return FillSchema (dataSet, schemaType, ((IDbDataAdapter) this).SelectCommand, srcTable, _behavior);
393                 }
394
395                 protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
396                 {
397                         if (dataTable == null)
398                                 throw new ArgumentNullException ("DataTable");
399
400                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
401                         if (command.Connection.State == ConnectionState.Closed) {
402                                 command.Connection.Open ();
403                                 behavior |= CommandBehavior.CloseConnection;
404                         }
405
406                         IDataReader reader = command.ExecuteReader (behavior);
407                         try {
408                                 string tableName =  SetupSchema (schemaType, dataTable.TableName);
409                                 if (tableName != null) {
410                                         // FillSchema should add the KeyInfo unless MissingSchemaAction
411                                         // is set to Ignore or Error.
412                                         MissingSchemaAction schemaAction = MissingSchemaAction;
413                                         if (!(schemaAction == MissingSchemaAction.Ignore ||
414                                                 schemaAction == MissingSchemaAction.Error))
415                                                 schemaAction = MissingSchemaAction.AddWithKey;
416
417                                         BuildSchema (reader, dataTable, schemaType, schemaAction,
418                                                 MissingMappingAction, TableMappings);
419                                 }
420                         } finally {
421                                 reader.Close ();
422                         }
423                         return dataTable;
424                 }
425
426                 protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
427                 {
428                         if (dataSet == null)
429                                 throw new ArgumentNullException ("DataSet");
430
431                         behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
432                         if (command.Connection.State == ConnectionState.Closed) {
433                                 command.Connection.Open ();
434                                 behavior |= CommandBehavior.CloseConnection;
435                         }
436
437                         IDataReader reader = command.ExecuteReader (behavior);
438                         ArrayList output = new ArrayList ();
439                         string tableName = srcTable;
440                         int index = 0;
441                         DataTable table;
442                         try {
443                                 // FillSchema should add the KeyInfo unless MissingSchemaAction
444                                 // is set to Ignore or Error.
445                                 MissingSchemaAction schemaAction = MissingSchemaAction;
446                                 if (!(MissingSchemaAction == MissingSchemaAction.Ignore ||
447                                         MissingSchemaAction == MissingSchemaAction.Error))
448                                         schemaAction = MissingSchemaAction.AddWithKey;
449
450                                 do {
451                                         tableName = SetupSchema (schemaType, tableName);
452                                         if (tableName != null) {
453                                                 if (dataSet.Tables.Contains (tableName))
454                                                         table = dataSet.Tables [tableName];
455                                                 else {
456                                                         // Do not create schema if MissingSchemAction is set to Ignore
457                                                         if (this.MissingSchemaAction == MissingSchemaAction.Ignore)
458                                                                 continue;
459                                                         table =  dataSet.Tables.Add (tableName);
460                                                 }
461                                                 
462                                                 BuildSchema (reader, table, schemaType, schemaAction,
463                                                         MissingMappingAction, TableMappings);
464                                                 output.Add (table);
465                                                 tableName = String.Format ("{0}{1}", srcTable, ++index);
466                                         }
467                                 }while (reader.NextResult ());
468                         } finally {
469                                 reader.Close ();
470                         }
471                         return (DataTable []) output.ToArray (typeof (DataTable));
472                 }
473
474                 [EditorBrowsable (EditorBrowsableState.Advanced)]
475                 public override IDataParameter[] GetFillParameters ()
476                 {
477                         IDbCommand selectCmd = ((IDbDataAdapter) this).SelectCommand;
478                         IDataParameter[] parameters = new IDataParameter [selectCmd.Parameters.Count];
479                         selectCmd.Parameters.CopyTo (parameters, 0);
480                         return parameters;
481                 }
482                 
483                 [MonoTODO]
484                 [Obsolete ("use 'protected DbDataAdapter(DbDataAdapter)' ctor")]
485                 object ICloneable.Clone ()
486                 {
487                         throw new NotImplementedException ();
488                 }
489
490                 public int Update (DataRow [] dataRows)
491                 {
492                         if (dataRows == null)
493                                 throw new ArgumentNullException("dataRows");
494                         
495                         if (dataRows.Length == 0)
496                                 return 0;
497
498                         if (dataRows [0] == null)
499                                 throw new ArgumentException("dataRows[0].");
500
501                         DataTable table = dataRows [0].Table;
502                         if (table == null)
503                                 throw new ArgumentException("table is null reference.");
504                         
505                         // all rows must be in the same table
506                         for (int i = 0; i < dataRows.Length; i++) {
507                                 if (dataRows [i] == null)
508                                         throw new ArgumentException ("dataRows[" + i + "].");
509                                 if (dataRows [i].Table != table)
510                                         throw new ArgumentException(
511                                                                     " DataRow["
512                                                                     + i
513                                                                     + "] is from a different DataTable than DataRow[0].");
514                         }
515                         
516                         // get table mapping for this rows
517                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
518                         if (tableMapping == null) {
519                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
520                                                                                                         TableMappings,
521                                                                                                         table.TableName,
522                                                                                                         table.TableName,
523                                                                                                         MissingMappingAction);
524                                 if (tableMapping != null) {
525                                         foreach (DataColumn col in table.Columns) {
526                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
527                                                         continue;
528                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
529                                                 if (columnMapping == null)
530                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
531                                                 tableMapping.ColumnMappings.Add (columnMapping);
532                                         }
533                                 } else {
534                                         ArrayList cmc = new ArrayList ();
535                                         foreach (DataColumn col in table.Columns)
536                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
537                                         tableMapping =
538                                                 new DataTableMapping (
539                                                                       table.TableName,
540                                                                       table.TableName,
541                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
542                                 }
543                         }
544
545                         DataRow[] copy = table.NewRowArray (dataRows.Length);
546                         Array.Copy (dataRows, 0, copy, 0, dataRows.Length);
547                         return Update (copy, tableMapping);
548                 }
549
550                 public override int Update (DataSet dataSet)
551                 {
552                         return Update (dataSet, DefaultSourceTableName);
553                 }
554
555                 public int Update (DataTable dataTable)
556                 {
557                         /*
558                           int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
559                           if (index < 0)
560                           throw new ArgumentException ();
561                           return Update (dataTable, TableMappings [index]);
562                         */
563                         DataTableMapping tableMapping = TableMappings.GetByDataSetTable (dataTable.TableName);
564                         if (tableMapping == null) {
565                                 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (
566                                                                                                          TableMappings,
567                                                                                                          dataTable.TableName,
568                                                                                                          dataTable.TableName,
569                                                                                                          MissingMappingAction);
570                                 if (tableMapping != null) {
571                                         foreach (DataColumn col in dataTable.Columns) {
572                                                 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
573                                                         continue;
574                                                 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
575                                                 if (columnMapping == null)
576                                                         columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
577                                                 tableMapping.ColumnMappings.Add (columnMapping);
578                                         }
579                                 } else {
580                                         ArrayList cmc = new ArrayList ();
581                                         foreach (DataColumn col in dataTable.Columns)
582                                                 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
583                                         tableMapping =
584                                                 new DataTableMapping (
585                                                                       dataTable.TableName,
586                                                                       dataTable.TableName,
587                                                                       cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
588                                 }
589                         }
590                         return Update (dataTable, tableMapping);
591                 }
592
593                 private int Update (DataTable dataTable, DataTableMapping tableMapping)
594                 {
595                         DataRow [] rows = dataTable.NewRowArray(dataTable.Rows.Count);
596                         dataTable.Rows.CopyTo (rows, 0);
597                         return Update (rows, tableMapping);
598                 }
599
600                 protected virtual int Update (DataRow [] dataRows, DataTableMapping tableMapping)
601                 {
602                         int updateCount = 0;
603                         foreach (DataRow row in dataRows) {
604                                 StatementType statementType = StatementType.Update;
605                                 IDbCommand command = null;
606                                 string commandName = String.Empty;
607
608                                 switch (row.RowState) {
609                                 case DataRowState.Added:
610                                         statementType = StatementType.Insert;
611                                         command = ((IDbDataAdapter) this).InsertCommand;
612                                         commandName = "Insert";
613                                         break;
614                                 case DataRowState.Deleted:
615                                         statementType = StatementType.Delete;
616                                         command = ((IDbDataAdapter) this).DeleteCommand;
617                                         commandName = "Delete";
618                                         break;
619                                 case DataRowState.Modified:
620                                         statementType = StatementType.Update;
621                                         command = ((IDbDataAdapter) this).UpdateCommand;
622                                         commandName = "Update";
623                                         break;
624                                 case DataRowState.Unchanged:
625                                 case DataRowState.Detached:
626                                         continue;
627                                 }
628
629                                 RowUpdatingEventArgs argsUpdating = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
630                                 row.RowError = null;
631                                 OnRowUpdating (argsUpdating);
632                                 switch (argsUpdating.Status) {
633                                 case UpdateStatus.Continue :
634                                         //continue in update operation
635                                         break;
636                                 case UpdateStatus.ErrorsOccurred :
637                                         if (argsUpdating.Errors == null)
638                                                 argsUpdating.Errors = ExceptionHelper.RowUpdatedError();
639                                         row.RowError += argsUpdating.Errors.Message;
640                                         if (!ContinueUpdateOnError)
641                                                 throw argsUpdating.Errors;
642                                         continue;
643                                 case UpdateStatus.SkipAllRemainingRows :
644                                         return updateCount;
645                                 case UpdateStatus.SkipCurrentRow :
646                                         updateCount++;
647                                         continue;
648                                 default :
649                                         throw ExceptionHelper.InvalidUpdateStatus (argsUpdating.Status);
650                                 }
651                                 command = argsUpdating.Command;
652                                 try {
653                                         if (command != null) {
654                                                 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
655 #if ONLY_1_1
656                                                 IDataParameter nullCheckParam = null;
657 #endif
658                                                 foreach (IDataParameter parameter in command.Parameters) {
659                                                         if ((parameter.Direction & ParameterDirection.Input) == 0)
660                                                                 continue;
661
662                                                         DataRowVersion rowVersion = parameter.SourceVersion;
663                                                         // Parameter version is ignored for non-update commands
664                                                         if (statementType == StatementType.Delete)
665                                                                 rowVersion = DataRowVersion.Original;
666
667                                                         string dsColumnName = parameter.SourceColumn;
668 #if NET_2_0
669                                                         if (columnMappings.Contains(dsColumnName)) {
670                                                                 dsColumnName = columnMappings [dsColumnName].DataSetColumn;
671                                                                 parameter.Value = row [dsColumnName, rowVersion];
672                                                         } else {
673                                                                 parameter.Value = null;
674                                                         }
675
676                                                         DbParameter nullCheckParam = parameter as DbParameter;
677 #else
678                                                         if (columnMappings.Contains(dsColumnName))
679                                                                 dsColumnName = columnMappings [dsColumnName].DataSetColumn;
680                                                         if (dsColumnName == null || dsColumnName.Length == 0) {
681                                                                 nullCheckParam = parameter;
682                                                                 continue;
683                                                         }
684                                                         parameter.Value = row [dsColumnName, rowVersion];
685 #endif
686
687 #if NET_2_0
688                                                         if (nullCheckParam != null && nullCheckParam.SourceColumnNullMapping) {
689 #else
690                                                         if (nullCheckParam != null) {
691 #endif
692                                                                 if (parameter.Value != null && parameter.Value != DBNull.Value)
693                                                                         nullCheckParam.Value = 0;
694                                                                 else
695                                                                         nullCheckParam.Value = 1;
696                                                                 nullCheckParam = null;
697                                                         }
698                                                 }
699                                         }
700                                 } catch (Exception e) {
701                                         argsUpdating.Errors = e;
702                                         argsUpdating.Status = UpdateStatus.ErrorsOccurred;
703                                 }
704
705                                 IDataReader reader = null;
706                                 try {
707                                         if (command == null)
708                                                 throw ExceptionHelper.UpdateRequiresCommand (commandName);
709                                 
710                                         CommandBehavior commandBehavior = CommandBehavior.Default;
711                                         if (command.Connection.State == ConnectionState.Closed) {
712                                                 command.Connection.Open ();
713                                                 commandBehavior |= CommandBehavior.CloseConnection;
714                                         }
715                                 
716                                         // use ExecuteReader because we want to use the commandbehavior parameter.
717                                         // so the connection will be closed if needed.
718                                         reader = command.ExecuteReader (commandBehavior);
719
720                                         // update the current row, if the update command returns any resultset
721                                         // ignore other than the first record.
722                                         DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
723
724                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
725                                             command.UpdatedRowSource == UpdateRowSource.FirstReturnedRecord) {
726                                                 if (reader.Read ()){
727                                                         DataTable retSchema = reader.GetSchemaTable ();
728                                                         foreach (DataRow dr in retSchema.Rows) {
729                                                                 string columnName = dr ["ColumnName"].ToString ();
730                                                                 string dstColumnName = columnName;
731                                                                 if (columnMappings != null &&
732                                                                     columnMappings.Contains(columnName))
733                                                                         dstColumnName = columnMappings [dstColumnName].DataSetColumn;
734                                                                 DataColumn dstColumn = row.Table.Columns [dstColumnName];
735                                                                 if (dstColumn == null
736                                                                     || (dstColumn.Expression != null
737                                                                         && dstColumn.Expression.Length > 0))
738                                                                         continue;
739                                                                 // info from : http://www.error-bank.com/microsoft.public.dotnet.framework.windowsforms.databinding/
740                                                                 // _35_hcsyiv0dha.2328@tk2msftngp10.phx.gbl_Thread.aspx
741                                                                 // disable readonly for non-expression columns.
742                                                                 bool readOnlyState = dstColumn.ReadOnly;
743                                                                 dstColumn.ReadOnly = false;
744                                                                 try {
745                                                                         row [dstColumnName] = reader [columnName];
746                                                                 } finally {
747                                                                         dstColumn.ReadOnly = readOnlyState;
748                                                                 }
749                                                         }
750                                                 }
751                                         }
752                                         reader.Close ();
753
754                                         int tmp = reader.RecordsAffected; // records affected is valid only after closing reader
755                                         // if the execute does not effect any rows we throw an exception.
756                                         if (tmp == 0)
757                                                 throw new DBConcurrencyException("Concurrency violation: the " + 
758                                                         commandName +"Command affected 0 records.", null,
759                                                         new DataRow [] { row });
760                                         updateCount += tmp;
761
762                                         if (command.UpdatedRowSource == UpdateRowSource.Both ||
763                                             command.UpdatedRowSource == UpdateRowSource.OutputParameters) {
764                                                 // Update output parameters to row values
765                                                 foreach (IDataParameter parameter in command.Parameters) {
766                                                         if (parameter.Direction != ParameterDirection.InputOutput
767                                                             && parameter.Direction != ParameterDirection.Output
768                                                             && parameter.Direction != ParameterDirection.ReturnValue)
769                                                                 continue;
770
771                                                         string dsColumnName = parameter.SourceColumn;
772                                                         if (columnMappings != null &&
773                                                             columnMappings.Contains(parameter.SourceColumn))
774                                                                 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
775                                                         DataColumn dstColumn = row.Table.Columns [dsColumnName];
776                                                         if (dstColumn == null
777                                                             || (dstColumn.Expression != null 
778                                                                 && dstColumn.Expression.Length > 0))
779                                                                 continue;
780                                                         bool readOnlyState = dstColumn.ReadOnly;
781                                                         dstColumn.ReadOnly  = false;
782                                                         try {
783                                                                 row [dsColumnName] = parameter.Value;
784                                                         } finally {
785                                                                 dstColumn.ReadOnly = readOnlyState;
786                                                         }
787                                                 }
788                                         }
789
790                                         RowUpdatedEventArgs updatedArgs = CreateRowUpdatedEvent (row, command, statementType, tableMapping);
791                                         OnRowUpdated (updatedArgs);
792                                         switch (updatedArgs.Status) {
793                                         case UpdateStatus.Continue:
794                                                 break;
795                                         case UpdateStatus.ErrorsOccurred:
796                                                 if (updatedArgs.Errors == null)
797                                                         updatedArgs.Errors = ExceptionHelper.RowUpdatedError();
798                                                 row.RowError += updatedArgs.Errors.Message;
799                                                 if (!ContinueUpdateOnError)
800                                                         throw updatedArgs.Errors;
801                                                 break;
802                                         case UpdateStatus.SkipCurrentRow:
803                                                 continue;
804                                         case UpdateStatus.SkipAllRemainingRows:
805                                                 return updateCount;
806                                         }
807 #if NET_2_0
808                                         if (!AcceptChangesDuringUpdate)
809                                                 continue;
810 #endif
811                                         row.AcceptChanges ();
812                                 } catch (Exception e) {
813                                         row.RowError = e.Message;
814                                         if (!ContinueUpdateOnError)
815                                                 throw e;
816                                 } finally {
817                                         if (reader != null && ! reader.IsClosed)
818                                                 reader.Close ();
819                                 }
820                         }
821                         return updateCount;
822                 }
823
824                 public int Update (DataSet dataSet, string srcTable)
825                 {
826                         MissingMappingAction mappingAction = MissingMappingAction;
827
828                         if (mappingAction == MissingMappingAction.Ignore)
829                                 mappingAction = MissingMappingAction.Error;
830
831                         DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, srcTable, srcTable, mappingAction);
832
833                         DataTable dataTable = dataSet.Tables [tableMapping.DataSetTable];
834                         if (dataTable == null)
835                                 throw new ArgumentException (String.Format ("Missing table {0}",
836                                                                             srcTable));
837                         return Update (dataTable, tableMapping);
838                 }
839
840 #if NET_2_0
841                 // All the batch methods, should be implemented, if supported,
842                 // by individual providers
843
844                 protected virtual int AddToBatch (IDbCommand command)
845                 {
846                         throw CreateMethodNotSupportedException ();
847                 }
848
849                 protected virtual void ClearBatch ()
850                 {
851                         throw CreateMethodNotSupportedException ();
852                 }
853
854                 protected virtual int ExecuteBatch ()
855                 {
856                         throw CreateMethodNotSupportedException ();
857                 }
858
859                 protected virtual IDataParameter GetBatchedParameter (int commandIdentifier, int parameterIndex)
860                 {
861                         throw CreateMethodNotSupportedException ();
862                 }
863
864                 protected virtual bool GetBatchedRecordsAffected (int commandIdentifier, out int recordsAffected, out Exception error)
865                 {
866                         recordsAffected = 1;
867                         error = null;
868                         return true;
869                 }
870
871                 protected virtual void InitializeBatching ()
872                 {
873                         throw CreateMethodNotSupportedException ();
874                 }
875
876                 protected virtual void TerminateBatching ()
877                 {
878                         throw CreateMethodNotSupportedException ();
879                 }
880
881                 Exception CreateMethodNotSupportedException ()
882                 {
883                         return new NotSupportedException ("Method is not supported.");
884                 }
885 #else
886                 internal override void OnFillErrorInternal (FillErrorEventArgs value)
887                 {
888                         OnFillError (value);
889                 }
890
891                 protected virtual void OnFillError (FillErrorEventArgs value)
892                 {
893                         if (FillError != null)
894                                 FillError (this, value);
895                 }
896 #endif
897                 #endregion // Methods
898         }
899 }