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