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