2 // System.Data.Common.DbDataAdapter.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Tim Coleman (tim@timcoleman.com)
7 // Sureshkumar T <tsureshkumar@novell.com>
10 // Copyright (C) Tim Coleman, 2002-2003
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
37 using System.Collections;
38 using System.ComponentModel;
40 using System.Runtime.InteropServices;
42 namespace System.Data.Common {
44 public abstract class DbDataAdapter : DataAdapter, IDbDataAdapter, IDataAdapter, ICloneable
46 public abstract class DbDataAdapter : DataAdapter, ICloneable
51 public const string DefaultSourceTableName = "Table";
52 const string DefaultSourceColumnName = "Column";
58 protected DbDataAdapter()
63 protected DbDataAdapter(DbDataAdapter adapter) : base(adapter)
73 protected virtual IDbConnection BaseConnection {
74 get { throw new NotImplementedException (); }
75 set { throw new NotImplementedException (); }
78 public IDbConnection Connection {
79 get { return BaseConnection; }
80 set { BaseConnection = value; }
86 protected internal CommandBehavior FillCommandBehavior {
87 get { throw new NotImplementedException (); }
88 set { throw new NotImplementedException (); }
95 protected virtual IDbCommand this [[Optional] StatementType statementType] {
96 get { throw new NotImplementedException (); }
97 set { throw new NotImplementedException (); }
101 protected virtual DbProviderFactory ProviderFactory {
102 get { throw new NotImplementedException (); }
106 IDbCommand IDbDataAdapter.SelectCommand {
107 get { throw new NotImplementedException(); }
108 set { throw new NotImplementedException(); }
112 IDbCommand IDbDataAdapter.UpdateCommand{
113 get { throw new NotImplementedException(); }
114 set { throw new NotImplementedException(); }
118 IDbCommand IDbDataAdapter.DeleteCommand{
119 get { throw new NotImplementedException(); }
120 set { throw new NotImplementedException(); }
124 IDbCommand IDbDataAdapter.InsertCommand{
125 get { throw new NotImplementedException(); }
126 set { throw new NotImplementedException(); }
130 public DbCommand SelectCommand {
131 get { throw new NotImplementedException(); }
132 set { throw new NotImplementedException(); }
136 public DbCommand DeleteCommand {
137 get { throw new NotImplementedException(); }
138 set { throw new NotImplementedException(); }
142 public DbCommand InsertCommand {
143 get { throw new NotImplementedException(); }
144 set { throw new NotImplementedException(); }
148 public DbCommand UpdateCommand {
149 get { throw new NotImplementedException(); }
150 set { throw new NotImplementedException(); }
154 public IDbTransaction Transaction {
155 get { throw new NotImplementedException (); }
156 set { throw new NotImplementedException (); }
160 public int UpdateBatchSize {
161 get { throw new NotImplementedException (); }
162 set { throw new NotImplementedException (); }
165 IDbCommand SelectCommand {
166 get { return ((IDbDataAdapter) this).SelectCommand; }
169 IDbCommand UpdateCommand {
170 get { return ((IDbDataAdapter) this).UpdateCommand; }
173 IDbCommand DeleteCommand {
174 get { return ((IDbDataAdapter) this).DeleteCommand; }
177 IDbCommand InsertCommand {
178 get { return ((IDbDataAdapter) this).InsertCommand; }
182 #endregion // Properties
186 #if ONLY_1_0 || ONLY_1_1
188 [DataCategory ("Fill")]
189 [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
190 public event FillErrorEventHandler FillError;
199 public virtual void BeginInit ()
201 throw new NotImplementedException ();
205 protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
206 protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
208 private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, object[] values, Exception e)
210 FillErrorEventArgs args = new FillErrorEventArgs (dataTable, values);
212 args.Continue = false;
216 protected override void Dispose (bool disposing)
219 IDbDataAdapter da = (IDbDataAdapter) this;
220 if (da.SelectCommand != null) {
221 da.SelectCommand.Dispose();
222 da.SelectCommand = null;
224 if (da.InsertCommand != null) {
225 da.InsertCommand.Dispose();
226 da.InsertCommand = null;
228 if (da.UpdateCommand != null) {
229 da.UpdateCommand.Dispose();
230 da.UpdateCommand = null;
232 if (da.DeleteCommand != null) {
233 da.DeleteCommand.Dispose();
234 da.DeleteCommand = null;
241 public virtual void EndInit ()
243 throw new NotImplementedException ();
247 public override int Fill (DataSet dataSet)
249 return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, CommandBehavior.Default);
252 public int Fill (DataTable dataTable)
254 if (dataTable == null)
255 throw new NullReferenceException ();
257 return Fill (dataTable, SelectCommand, CommandBehavior.Default);
260 public int Fill (DataSet dataSet, string srcTable)
262 return Fill (dataSet, 0, 0, srcTable, SelectCommand, CommandBehavior.Default);
266 protected override int Fill (DataTable dataTable, IDataReader dataReader)
268 protected virtual int Fill (DataTable dataTable, IDataReader dataReader)
271 if (dataReader.FieldCount == 0) {
279 string tableName = SetupSchema (SchemaType.Mapped, dataTable.TableName);
280 if (tableName != null) {
281 dataTable.TableName = tableName;
282 FillTable (dataTable, dataReader, 0, 0, ref count);
291 protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
293 CommandBehavior commandBehavior = behavior;
294 // first see that the connection is not close.
295 if (command.Connection.State == ConnectionState.Closed)
297 command.Connection.Open ();
298 commandBehavior |= CommandBehavior.CloseConnection;
300 return Fill (dataTable, command.ExecuteReader (commandBehavior));
305 public int Fill (int startRecord, int maxRecords, DataTable[] dataTables)
307 throw new NotImplementedException ();
311 public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable)
313 return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default);
318 protected virtual int Fill (DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior)
320 throw new NotImplementedException ();
325 protected override int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
327 protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
331 throw new ArgumentException ("The startRecord parameter was less than 0.");
333 throw new ArgumentException ("The maxRecords parameter was less than 0.");
340 string tableName = srcTable;
342 // Non-resultset queries like insert, delete or update aren't processed.
343 if (dataReader.FieldCount != -1)
345 tableName = SetupSchema (SchemaType.Mapped, tableName);
346 if (tableName != null) {
348 // check if the table exists in the dataset
349 if (dataSet.Tables.Contains (tableName))
350 // get the table from the dataset
351 dataTable = dataSet.Tables [tableName];
353 dataTable = new DataTable(tableName);
354 dataSet.Tables.Add (dataTable);
357 if (!FillTable (dataTable, dataReader, startRecord, maxRecords, ref count)) {
361 tableName = String.Format ("{0}{1}", srcTable, ++resultIndex);
367 } while (dataReader.NextResult ());
376 protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
378 if (MissingSchemaAction == MissingSchemaAction.AddWithKey)
379 behavior |= CommandBehavior.KeyInfo;
380 CommandBehavior commandBehavior = behavior;
381 if (command.Connection.State == ConnectionState.Closed) {
382 command.Connection.Open ();
383 commandBehavior |= CommandBehavior.CloseConnection;
385 return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
388 private bool FillTable (DataTable dataTable, IDataReader dataReader, int startRecord, int maxRecords, ref int counter)
390 if (dataReader.FieldCount == 0)
393 int counterStart = counter;
395 int[] mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
397 int[] sortedMapping = new int[mapping.Length];
398 int length = sortedMapping.Length;
399 for(int i=0; i < sortedMapping.Length; i++) {
401 sortedMapping[mapping[i]] = i;
403 sortedMapping[--length] = i;
406 for (int i = 0; i < startRecord; i++) {
410 while (dataReader.Read () && (maxRecords == 0 || (counter - counterStart) < maxRecords)) {
412 dataTable.BeginLoadData ();
413 dataTable.LoadDataRow (dataReader, sortedMapping, length, AcceptChangesDuringFill);
414 dataTable.EndLoadData ();
417 catch (Exception e) {
418 object[] readerArray = new object[dataReader.FieldCount];
419 object[] tableArray = new object[mapping.Length];
420 // we get the values from the datareader
421 dataReader.GetValues (readerArray);
422 // copy from datareader columns to table columns according to given mapping
423 for (int i = 0; i < mapping.Length; i++) {
424 if (mapping[i] >= 0) {
425 tableArray[i] = readerArray[mapping[i]];
428 FillErrorEventArgs args = CreateFillErrorEvent (dataTable, tableArray, e);
440 /// Fills the given datatable using values from reader. if a value
441 /// for a column is null, that will be filled with default value.
443 /// <returns>No. of rows affected </returns>
444 internal static int FillFromReader (DataTable table,
449 LoadOption loadOption
452 if (reader.FieldCount == 0)
455 for (int i = 0; i < start; i++)
459 object [] values = new object [mapping.Length];
460 while (reader.Read () &&
461 (length == 0 || counter < length)) {
463 for (int i = 0 ; i < mapping.Length; i++)
464 values [i] = mapping [i] < 0 ? null : reader [mapping [i]];
466 table.BeginLoadData ();
467 table.LoadDataRow (values, loadOption);
468 table.EndLoadData ();
476 public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType)
478 return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
481 public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
483 return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default);
486 public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
488 return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default);
491 [MonoTODO ("Verify")]
492 protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
494 behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
495 if (command.Connection.State == ConnectionState.Closed) {
496 command.Connection.Open ();
497 behavior |= CommandBehavior.CloseConnection;
500 IDataReader reader = command.ExecuteReader (behavior);
503 string tableName = SetupSchema (schemaType, dataTable.TableName);
504 if (tableName != null)
506 BuildSchema (reader, dataTable, schemaType);
516 [MonoTODO ("Verify")]
517 protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
519 behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
520 if (command.Connection.State == ConnectionState.Closed) {
521 command.Connection.Open ();
522 behavior |= CommandBehavior.CloseConnection;
525 IDataReader reader = command.ExecuteReader (behavior);
526 ArrayList output = new ArrayList ();
527 string tableName = srcTable;
532 tableName = SetupSchema (schemaType, tableName);
533 if (tableName != null)
535 if (dataSet.Tables.Contains (tableName))
536 table = dataSet.Tables [tableName];
539 table = new DataTable(tableName);
540 dataSet.Tables.Add (table);
542 BuildSchema (reader, table, schemaType);
544 tableName = String.Format ("{0}{1}", srcTable, ++index);
551 return (DataTable[]) output.ToArray (typeof (DataTable));
556 public DataSet GetDataSet ()
558 throw new NotImplementedException ();
562 public DataTable GetDataTable ()
564 throw new NotImplementedException ();
568 private string SetupSchema (SchemaType schemaType, string sourceTableName)
570 DataTableMapping tableMapping = null;
572 if (schemaType == SchemaType.Mapped)
574 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, sourceTableName, MissingMappingAction);
576 if (tableMapping != null)
577 return tableMapping.DataSetTable;
581 return sourceTableName;
584 [EditorBrowsable (EditorBrowsableState.Advanced)]
585 public override IDataParameter[] GetFillParameters ()
587 IDataParameter[] parameters = new IDataParameter[SelectCommand.Parameters.Count];
588 SelectCommand.Parameters.CopyTo (parameters, 0);
592 // this method builds the schema for a given datatable. it returns a int array with
593 // "array[ordinal of datatable column] == index of source column in data reader".
594 // each column in the datatable has a mapping to a specific column in the datareader,
595 // the int array represents this match.
597 private int[] BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
599 return BuildSchema (reader, table, schemaType, MissingSchemaAction,
600 MissingMappingAction, TableMappings);
604 /// Creates or Modifies the schema of the given DataTable based on the schema of
605 /// the reader and the arguments passed.
607 internal static int[] BuildSchema (IDataReader reader,
609 SchemaType schemaType,
610 MissingSchemaAction missingSchAction,
611 MissingMappingAction missingMapAction,
612 DataTableMappingCollection dtMapping
616 // FIXME : this fails if query has fewer columns than a table
617 int[] mapping = new int[table.Columns.Count]; // mapping the reader indexes to the datatable indexes
619 for(int i=0; i < mapping.Length; i++) {
623 ArrayList primaryKey = new ArrayList ();
624 ArrayList sourceColumns = new ArrayList ();
625 bool createPrimaryKey = true;
627 DataTable schemaTable = reader.GetSchemaTable ();
629 DataColumn ColumnNameCol = ((colIndex = schemaTable.Columns.IndexOf("ColumnName")) >= 0) ? schemaTable.Columns[colIndex] : null;
630 DataColumn DataTypeCol = ((colIndex = schemaTable.Columns.IndexOf("DataType")) >= 0) ? schemaTable.Columns[colIndex] : null;
631 DataColumn IsAutoIncrementCol = ((colIndex = schemaTable.Columns.IndexOf("IsAutoIncrement")) >= 0) ? schemaTable.Columns[colIndex] : null;
632 DataColumn AllowDBNullCol = ((colIndex = schemaTable.Columns.IndexOf("AllowDBNull")) >= 0) ? schemaTable.Columns[colIndex] : null;
633 DataColumn IsReadOnlyCol = ((colIndex = schemaTable.Columns.IndexOf("IsReadOnly")) >= 0) ? schemaTable.Columns[colIndex] : null;
634 DataColumn IsKeyCol = ((colIndex = schemaTable.Columns.IndexOf("IsKey")) >= 0) ? schemaTable.Columns[colIndex] : null;
635 DataColumn IsUniqueCol = ((colIndex = schemaTable.Columns.IndexOf("IsUnique")) >= 0) ? schemaTable.Columns[colIndex] : null;
636 DataColumn ColumnSizeCol = ((colIndex = schemaTable.Columns.IndexOf("ColumnSize")) >= 0) ? schemaTable.Columns[colIndex] : null;
638 foreach (DataRow schemaRow in schemaTable.Rows) {
639 // generate a unique column name in the source table.
640 string sourceColumnName;
641 if (ColumnNameCol == null || schemaRow.IsNull(ColumnNameCol))
642 sourceColumnName = DefaultSourceColumnName;
644 sourceColumnName = (string) schemaRow [ColumnNameCol];
646 string realSourceColumnName = sourceColumnName;
648 for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1)
649 realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i);
650 sourceColumns.Add(realSourceColumnName);
652 // generate DataSetColumnName from DataTableMapping, if any
653 string dsColumnName = realSourceColumnName;
654 DataTableMapping tableMapping = null;
655 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (dtMapping, table.TableName, table.TableName, missingMapAction);
656 if (tableMapping != null)
659 table.TableName = tableMapping.DataSetTable;
660 // check to see if the column mapping exists
661 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, missingMapAction);
662 if (columnMapping != null)
664 Type columnType = (Type)schemaRow[DataTypeCol];
666 columnMapping.GetDataColumnBySchemaAction(
673 // if the column is not in the table - add it.
674 if (table.Columns.IndexOf(col) == -1)
676 if (missingSchAction == MissingSchemaAction.Add
677 || missingSchAction == MissingSchemaAction.AddWithKey)
678 table.Columns.Add(col);
680 int[] tmp = new int[mapping.Length + 1];
681 Array.Copy(mapping,0,tmp,0,col.Ordinal);
682 Array.Copy(mapping,col.Ordinal,tmp,col.Ordinal + 1,mapping.Length - col.Ordinal);
686 object value = (AllowDBNullCol != null) ? schemaRow[AllowDBNullCol] : null;
687 bool allowDBNull = value is bool ? (bool)value : true;
688 col.AllowDBNull = allowDBNull;
689 value = (IsKeyCol != null) ? schemaRow[IsKeyCol] : null;
690 bool isKey = value is bool ? (bool)value : false;
692 if (missingSchAction == MissingSchemaAction.AddWithKey) {
694 value = (IsAutoIncrementCol != null) ? schemaRow[IsAutoIncrementCol] : null;
695 bool isAutoIncrement = value is bool ? (bool)value : false;
696 value = (IsReadOnlyCol != null) ? schemaRow[IsReadOnlyCol] : null;
697 bool isReadOnly = value is bool ? (bool)value : false;
698 value = (IsUniqueCol != null) ? schemaRow[IsUniqueCol] : null;
699 bool isUnique = value is bool ? (bool)value : false;
701 // fill woth key info
702 if (isAutoIncrement && DataColumn.CanAutoIncrement(columnType)) {
703 col.AutoIncrement = true;
705 col.AllowDBNull = false;
708 if (columnType == DbTypes.TypeOfString) {
709 col.MaxLength = (ColumnSizeCol != null) ? (int)schemaRow[ColumnSizeCol] : 0;
715 if (!allowDBNull && (!isReadOnly || isKey))
716 col.AllowDBNull = false;
717 if (isUnique && !isKey && !columnType.IsArray) {
720 col.AllowDBNull = false;
725 primaryKey.Add (col);
727 createPrimaryKey = false;
730 // add the ordinal of the column as a key and the index of the column in the datareader as a value.
731 mapping[col.Ordinal] = readerIndex++;
736 if (primaryKey.Count > 0) {
737 DataColumn[] colKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
738 if (createPrimaryKey)
739 table.PrimaryKey = colKey;
741 UniqueConstraint uConstraint = new UniqueConstraint(colKey);
742 for (int i = 0; i < table.Constraints.Count; i++) {
743 if (table.Constraints[i].Equals(uConstraint)) {
749 if (uConstraint != null)
750 table.Constraints.Add(uConstraint);
759 object ICloneable.Clone ()
761 throw new NotImplementedException ();
765 public int Update (DataRow[] dataRows)
767 if (dataRows == null)
768 throw new ArgumentNullException("dataRows");
770 if (dataRows.Length == 0)
773 if (dataRows[0] == null)
774 throw new ArgumentException("dataRows[0].");
776 DataTable table = dataRows[0].Table;
778 throw new ArgumentException("table is null reference.");
780 // all rows must be in the same table
781 for (int i = 0; i < dataRows.Length; i++)
783 if (dataRows[i] == null)
784 throw new ArgumentException("dataRows[" + i + "].");
785 if (dataRows[i].Table != table)
786 throw new ArgumentException(
789 + "] is from a different DataTable than DataRow[0].");
792 // get table mapping for this rows
793 DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
794 if (tableMapping == null)
796 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
800 MissingMappingAction);
801 if (tableMapping != null) {
802 foreach (DataColumn col in table.Columns) {
803 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
805 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
806 if (columnMapping == null)
807 columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
808 tableMapping.ColumnMappings.Add (columnMapping);
811 ArrayList cmc = new ArrayList ();
812 foreach (DataColumn col in table.Columns)
813 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
815 new DataTableMapping (
818 cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
822 DataRow[] copy = table.NewRowArray(dataRows.Length);
823 Array.Copy(dataRows, 0, copy, 0, dataRows.Length);
824 return Update(copy, tableMapping);
827 public override int Update (DataSet dataSet)
829 return Update (dataSet, DefaultSourceTableName);
832 public int Update (DataTable dataTable)
835 int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
837 throw new ArgumentException ();
838 return Update (dataTable, TableMappings [index]);
840 DataTableMapping tableMapping = TableMappings.GetByDataSetTable (dataTable.TableName);
841 if (tableMapping == null)
843 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (
847 MissingMappingAction);
848 if (tableMapping != null) {
849 foreach (DataColumn col in dataTable.Columns) {
850 if (tableMapping.ColumnMappings.IndexOf (col.ColumnName) >= 0)
852 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction (tableMapping.ColumnMappings, col.ColumnName, MissingMappingAction);
853 if (columnMapping == null)
854 columnMapping = new DataColumnMapping (col.ColumnName, col.ColumnName);
855 tableMapping.ColumnMappings.Add (columnMapping);
858 ArrayList cmc = new ArrayList ();
859 foreach (DataColumn col in dataTable.Columns)
860 cmc.Add (new DataColumnMapping (col.ColumnName, col.ColumnName));
862 new DataTableMapping (
865 cmc.ToArray (typeof (DataColumnMapping)) as DataColumnMapping []);
868 return Update (dataTable, tableMapping);
871 private int Update (DataTable dataTable, DataTableMapping tableMapping)
873 DataRow[] rows = dataTable.NewRowArray(dataTable.Rows.Count);
874 dataTable.Rows.CopyTo (rows, 0);
875 return Update (rows, tableMapping);
879 protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping)
882 foreach (DataRow row in dataRows) {
883 StatementType statementType = StatementType.Update;
884 IDbCommand command = null;
885 string commandName = String.Empty;
887 switch (row.RowState) {
888 case DataRowState.Added:
889 statementType = StatementType.Insert;
890 command = InsertCommand;
891 commandName = "Insert";
893 case DataRowState.Deleted:
894 statementType = StatementType.Delete;
895 command = DeleteCommand;
896 commandName = "Delete";
898 case DataRowState.Modified:
899 statementType = StatementType.Update;
900 command = UpdateCommand;
901 commandName = "Update";
903 case DataRowState.Unchanged:
904 case DataRowState.Detached:
908 RowUpdatingEventArgs argsUpdating = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
910 OnRowUpdating(argsUpdating);
911 switch(argsUpdating.Status) {
912 case UpdateStatus.Continue :
913 //continue in update operation
915 case UpdateStatus.ErrorsOccurred :
916 if (argsUpdating.Errors == null) {
917 argsUpdating.Errors = ExceptionHelper.RowUpdatedError();
919 row.RowError += argsUpdating.Errors.Message;
920 if (!ContinueUpdateOnError) {
921 throw argsUpdating.Errors;
924 case UpdateStatus.SkipAllRemainingRows :
926 case UpdateStatus.SkipCurrentRow :
930 throw ExceptionHelper.InvalidUpdateStatus(argsUpdating.Status);
932 command = argsUpdating.Command;
934 if (command != null) {
935 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
936 foreach (IDataParameter parameter in command.Parameters) {
937 if ((parameter.Direction & ParameterDirection.Input) != 0) {
938 string dsColumnName = parameter.SourceColumn;
939 if (columnMappings.Contains(parameter.SourceColumn))
940 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
941 if (dsColumnName == null || dsColumnName.Length <= 0)
944 DataRowVersion rowVersion = parameter.SourceVersion;
945 // Parameter version is ignored for non-update commands
946 if (statementType == StatementType.Delete)
947 rowVersion = DataRowVersion.Original;
949 parameter.Value = row [dsColumnName, rowVersion];
954 catch (Exception e) {
955 argsUpdating.Errors = e;
956 argsUpdating.Status = UpdateStatus.ErrorsOccurred;
960 IDataReader reader = null;
962 if (command == null) {
963 throw ExceptionHelper.UpdateRequiresCommand(commandName);
966 CommandBehavior commandBehavior = CommandBehavior.Default;
967 if (command.Connection.State == ConnectionState.Closed) {
968 command.Connection.Open ();
969 commandBehavior |= CommandBehavior.CloseConnection;
972 // use ExecuteReader because we want to use the commandbehavior parameter.
973 // so the connection will be closed if needed.
974 reader = command.ExecuteReader (commandBehavior);
976 // update the current row, if the update command returns any resultset
977 // ignore other than the first record.
978 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
980 if (command.UpdatedRowSource == UpdateRowSource.Both ||
981 command.UpdatedRowSource == UpdateRowSource.FirstReturnedRecord) {
983 DataTable retSchema = reader.GetSchemaTable ();
984 foreach (DataRow dr in retSchema.Rows) {
985 string columnName = dr ["ColumnName"].ToString ();
986 string dstColumnName = columnName;
987 if (columnMappings != null &&
988 columnMappings.Contains(columnName))
989 dstColumnName = columnMappings [dstColumnName].DataSetColumn;
990 DataColumn dstColumn = row.Table.Columns [dstColumnName];
991 if (dstColumn == null
992 || (dstColumn.Expression != null
993 && dstColumn.Expression.Length > 0))
995 // info from : http://www.error-bank.com/microsoft.public.dotnet.framework.windowsforms.databinding/
996 // _35_hcsyiv0dha.2328@tk2msftngp10.phx.gbl_Thread.aspx
997 // disable readonly for non-expression columns.
998 bool readOnlyState = dstColumn.ReadOnly;
999 dstColumn.ReadOnly = false;
1001 row [dstColumnName] = reader [columnName];
1003 dstColumn.ReadOnly = readOnlyState;
1010 int tmp = reader.RecordsAffected; // records affected is valid only after closing reader
1011 // if the execute does not effect any rows we throw an exception.
1013 throw new DBConcurrencyException("Concurrency violation: the " +
1014 commandName +"Command affected 0 records.");
1017 if (command.UpdatedRowSource == UpdateRowSource.Both ||
1018 command.UpdatedRowSource == UpdateRowSource.OutputParameters) {
1019 // Update output parameters to row values
1020 foreach (IDataParameter parameter in command.Parameters) {
1021 if (parameter.Direction != ParameterDirection.InputOutput
1022 && parameter.Direction != ParameterDirection.Output
1023 && parameter.Direction != ParameterDirection.ReturnValue)
1026 string dsColumnName = parameter.SourceColumn;
1027 if (columnMappings != null &&
1028 columnMappings.Contains(parameter.SourceColumn))
1029 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
1030 DataColumn dstColumn = row.Table.Columns [dsColumnName];
1031 if (dstColumn == null
1032 || (dstColumn.Expression != null
1033 && dstColumn.Expression.Length > 0))
1035 bool readOnlyState = dstColumn.ReadOnly;
1036 dstColumn.ReadOnly = false;
1038 row [dsColumnName] = parameter.Value;
1040 dstColumn.ReadOnly = readOnlyState;
1046 RowUpdatedEventArgs updatedArgs = CreateRowUpdatedEvent(row, command, statementType, tableMapping);
1047 OnRowUpdated(updatedArgs);
1048 switch(updatedArgs.Status) {
1049 case UpdateStatus.Continue:
1051 case UpdateStatus.ErrorsOccurred:
1052 if (updatedArgs.Errors == null) {
1053 updatedArgs.Errors = ExceptionHelper.RowUpdatedError();
1055 row.RowError += updatedArgs.Errors.Message;
1056 if (!ContinueUpdateOnError) {
1057 throw updatedArgs.Errors;
1060 case UpdateStatus.SkipCurrentRow:
1062 case UpdateStatus.SkipAllRemainingRows:
1065 row.AcceptChanges ();
1066 } catch(Exception e) {
1067 row.RowError = e.Message;
1068 if (!ContinueUpdateOnError) {
1072 if (reader != null && ! reader.IsClosed) {
1080 public int Update (DataSet dataSet, string sourceTable)
1082 MissingMappingAction mappingAction = MissingMappingAction;
1084 if (mappingAction == MissingMappingAction.Ignore)
1085 mappingAction = MissingMappingAction.Error;
1087 DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction);
1089 DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable];
1090 if (dataTable == null)
1091 throw new ArgumentException (String.Format ("Missing table {0}",
1093 return Update (dataTable, tableMapping);
1096 #if ONLY_1_0 || ONLY_1_1
1097 protected virtual void OnFillError (FillErrorEventArgs value)
1099 if (FillError != null)
1100 FillError (this, value);
1104 protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
1105 protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
1107 #endregion // Methods