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