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