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