2 // System.Data.Common.DbDataAdapter.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Tim Coleman (tim@timcoleman.com)
9 // Copyright (C) Tim Coleman, 2002-2003
13 using System.Collections;
14 using System.ComponentModel;
16 using System.Runtime.InteropServices;
18 namespace System.Data.Common {
19 public abstract class DbDataAdapter : DataAdapter, ICloneable
23 public const string DefaultSourceTableName = "Table";
24 const string DefaultSourceColumnName = "Column";
30 protected DbDataAdapter()
40 protected virtual IDbConnection BaseConnection {
41 get { throw new NotImplementedException (); }
42 set { throw new NotImplementedException (); }
45 public IDbConnection Connection {
46 get { return BaseConnection; }
47 set { BaseConnection = value; }
51 IDbCommand DeleteCommand {
52 get { return ((IDbDataAdapter) this).DeleteCommand; }
56 protected internal CommandBehavior FillCommandBehavior {
57 get { throw new NotImplementedException (); }
58 set { throw new NotImplementedException (); }
62 IDbCommand InsertCommand {
63 get { return ((IDbDataAdapter) this).InsertCommand; }
68 protected virtual IDbCommand this [[Optional] StatementType statementType] {
69 get { throw new NotImplementedException (); }
70 set { throw new NotImplementedException (); }
73 protected virtual DbProviderFactory ProviderFactory {
74 get { throw new NotImplementedException (); }
78 IDbCommand SelectCommand {
79 get { return ((IDbDataAdapter) this).SelectCommand; }
84 public IDbTransaction Transaction {
85 get { throw new NotImplementedException (); }
86 set { throw new NotImplementedException (); }
90 public int UpdateBatchSize {
91 get { throw new NotImplementedException (); }
92 set { throw new NotImplementedException (); }
96 IDbCommand UpdateCommand {
97 get { return ((IDbDataAdapter) this).UpdateCommand; }
100 #endregion // Properties
104 #if ONLY_1_0 || ONLY_1_1
106 [DataCategory ("Fill")]
107 [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")]
108 public event FillErrorEventHandler FillError;
117 public virtual void BeginInit ()
119 throw new NotImplementedException ();
123 protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
124 protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping);
126 private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, object[] values, Exception e)
128 FillErrorEventArgs args = new FillErrorEventArgs (dataTable, values);
130 args.Continue = false;
134 protected override void Dispose (bool disposing)
137 IDbDataAdapter da = (IDbDataAdapter) this;
138 if (da.SelectCommand != null) {
139 da.SelectCommand.Dispose();
140 da.SelectCommand = null;
142 if (da.InsertCommand != null) {
143 da.InsertCommand.Dispose();
144 da.InsertCommand = null;
146 if (da.UpdateCommand != null) {
147 da.UpdateCommand.Dispose();
148 da.UpdateCommand = null;
150 if (da.DeleteCommand != null) {
151 da.DeleteCommand.Dispose();
152 da.DeleteCommand = null;
159 public virtual void EndInit ()
161 throw new NotImplementedException ();
165 public override int Fill (DataSet dataSet)
167 return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, CommandBehavior.Default);
170 public int Fill (DataTable dataTable)
172 if (dataTable == null)
173 throw new NullReferenceException ();
175 return Fill (dataTable, SelectCommand, CommandBehavior.Default);
178 public int Fill (DataSet dataSet, string srcTable)
180 return Fill (dataSet, 0, 0, srcTable, SelectCommand, CommandBehavior.Default);
184 protected override int Fill (DataTable dataTable, IDataReader dataReader)
186 protected virtual int Fill (DataTable dataTable, IDataReader dataReader)
190 bool doContinue = true;
192 if (dataReader.FieldCount == 0) {
199 object[] itemArray = new object [dataReader.FieldCount];
200 string tableName = SetupSchema (SchemaType.Mapped, dataTable.TableName);
201 if (tableName != null)
203 dataTable.TableName = tableName;
204 Hashtable mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
206 while (doContinue && dataReader.Read ())
208 // we get the values from the datareader
209 dataReader.GetValues (itemArray);
211 // we only need the values that has a mapping to the table.
212 object[] tableArray = new object[mapping.Count];
213 for (int i = 0; i < tableArray.Length; i++)
214 tableArray[i] = mapping[i]; // get the value for each column
218 dataTable.BeginLoadData ();
219 dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
220 dataTable.EndLoadData ();
225 FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
227 doContinue = args.Continue;
240 protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior)
242 CommandBehavior commandBehavior = behavior;
243 // first see that the connection is not close.
244 if (command.Connection.State == ConnectionState.Closed)
246 command.Connection.Open ();
247 commandBehavior |= CommandBehavior.CloseConnection;
249 return Fill (dataTable, command.ExecuteReader (commandBehavior));
254 public int Fill (int startRecord, int maxRecords, DataTable[] dataTables)
256 throw new NotImplementedException ();
260 public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable)
262 return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default);
267 protected virtual int Fill (DataTable[] dataTables, int startRecord, int maxRecords, IDbCommand command, CommandBehavior behavior)
269 throw new NotImplementedException ();
274 protected override int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
276 protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords)
280 throw new ArgumentException ("The startRecord parameter was less than 0.");
282 throw new ArgumentException ("The maxRecords parameter was less than 0.");
284 if (dataReader.FieldCount == 0) {
292 bool doContinue = true;
296 string tableName = srcTable;
301 tableName = SetupSchema (SchemaType.Mapped, tableName);
302 if (tableName != null)
304 // check if the table exists in the dataset
305 if (dataSet.Tables.Contains (tableName))
306 // get the table from the dataset
307 dataTable = dataSet.Tables [tableName];
310 dataTable = new DataTable(tableName);
311 dataSet.Tables.Add (dataTable);
313 Hashtable mapping = BuildSchema (dataReader, dataTable, SchemaType.Mapped);
315 for (int i = 0; i < startRecord; i += 1)
318 itemArray = new object [dataReader.FieldCount];
320 while (doContinue && dataReader.Read () && !(maxRecords > 0 && count >= maxRecords))
322 // we get the values from the datareader
323 dataReader.GetValues (itemArray);
325 // we only need the values that has a mapping to the table.
326 object[] tableArray = new object[mapping.Count];
327 for (int i = 0; i < tableArray.Length; i++)
328 tableArray[i] = itemArray[(int)mapping[i]]; // get the value for each column
332 dataTable.BeginLoadData ();
333 //dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
334 dataTable.LoadDataRow (tableArray, AcceptChangesDuringFill);
335 dataTable.EndLoadData ();
340 FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e);
342 doContinue = args.Continue;
346 tableName = String.Format ("{0}{1}", srcTable, ++resultIndex);
352 } while (doContinue && dataReader.NextResult ());
363 protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior)
365 if (MissingSchemaAction == MissingSchemaAction.AddWithKey)
366 behavior |= CommandBehavior.KeyInfo;
367 CommandBehavior commandBehavior = behavior;
368 if (command.Connection.State == ConnectionState.Closed) {
369 command.Connection.Open ();
370 commandBehavior |= CommandBehavior.CloseConnection;
372 return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords);
375 public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType)
377 return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default);
380 public DataTable FillSchema (DataTable dataTable, SchemaType schemaType)
382 return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default);
385 public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable)
387 return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default);
390 [MonoTODO ("Verify")]
391 protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior)
393 behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
394 if (command.Connection.State == ConnectionState.Closed) {
395 command.Connection.Open ();
396 behavior |= CommandBehavior.CloseConnection;
399 IDataReader reader = command.ExecuteReader (behavior);
402 string tableName = SetupSchema (schemaType, dataTable.TableName);
403 if (tableName != null)
405 BuildSchema (reader, dataTable, schemaType);
415 [MonoTODO ("Verify")]
416 protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior)
418 behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
419 if (command.Connection.State == ConnectionState.Closed) {
420 command.Connection.Open ();
421 behavior |= CommandBehavior.CloseConnection;
424 IDataReader reader = command.ExecuteReader (behavior);
425 ArrayList output = new ArrayList ();
426 string tableName = srcTable;
431 tableName = SetupSchema (schemaType, tableName);
432 if (tableName != null)
434 if (dataSet.Tables.Contains (tableName))
435 table = dataSet.Tables [tableName];
438 table = new DataTable(tableName);
439 dataSet.Tables.Add (table);
441 BuildSchema (reader, table, schemaType);
443 tableName = String.Format ("{0}{1}", srcTable, ++index);
450 return (DataTable[]) output.ToArray (typeof (DataTable));
455 public DataSet GetDataSet ()
457 throw new NotImplementedException ();
461 public DataTable GetDataTable ()
463 throw new NotImplementedException ();
467 private string SetupSchema (SchemaType schemaType, string sourceTableName)
469 DataTableMapping tableMapping = null;
471 if (schemaType == SchemaType.Mapped)
473 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, sourceTableName, MissingMappingAction);
475 if (tableMapping != null)
476 return tableMapping.DataSetTable;
480 return sourceTableName;
483 [EditorBrowsable (EditorBrowsableState.Advanced)]
484 public override IDataParameter[] GetFillParameters ()
486 IDataParameter[] parameters = new IDataParameter[SelectCommand.Parameters.Count];
487 SelectCommand.Parameters.CopyTo (parameters, 0);
491 // this method bulds the schema for a given datatable
492 // returns a hashtable that his keys are the ordinal of the datatable columns, and his values
493 // are the indexes of the source columns in the data reader.
494 // each column in the datatable has a mapping to a specific column in the datareader
495 // the hashtable represents this match.
497 private Hashtable BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType)
500 Hashtable mapping = new Hashtable(); // hashing the reader indexes with the datatable indexes
501 ArrayList primaryKey = new ArrayList ();
502 ArrayList sourceColumns = new ArrayList ();
504 foreach (DataRow schemaRow in reader.GetSchemaTable ().Rows) {
505 // generate a unique column name in the source table.
506 string sourceColumnName;
507 if (schemaRow ["ColumnName"].Equals (DBNull.Value))
508 sourceColumnName = DefaultSourceColumnName;
510 sourceColumnName = (string) schemaRow ["ColumnName"];
512 string realSourceColumnName = sourceColumnName;
514 for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1)
515 realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i);
516 sourceColumns.Add(realSourceColumnName);
518 // generate DataSetColumnName from DataTableMapping, if any
519 string dsColumnName = realSourceColumnName;
520 DataTableMapping tableMapping = null;
521 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, table.TableName, table.TableName, MissingMappingAction);
522 if (tableMapping != null)
525 table.TableName = tableMapping.DataSetTable;
526 // check to see if the column mapping exists
527 DataColumnMapping columnMapping = DataColumnMappingCollection.GetColumnMappingBySchemaAction(tableMapping.ColumnMappings, realSourceColumnName, MissingMappingAction);
528 if (columnMapping != null)
531 columnMapping.GetDataColumnBySchemaAction(
533 (Type)schemaRow["DataType"],
534 MissingSchemaAction);
538 // if the column is not in the table - add it.
539 if (table.Columns.IndexOf(col) == -1)
541 if (MissingSchemaAction == MissingSchemaAction.Add || MissingSchemaAction == MissingSchemaAction.AddWithKey)
542 table.Columns.Add(col);
545 if (!schemaRow["IsKey"].Equals (DBNull.Value))
546 if ((bool) (schemaRow ["IsKey"]))
547 primaryKey.Add (col);
549 // add the ordinal of the column as a key and the index of the column in the datareader as a value.
550 mapping.Add(col.Ordinal, readerIndex);
556 if (primaryKey.Count > 0)
557 table.PrimaryKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn)));
563 object ICloneable.Clone ()
565 throw new NotImplementedException ();
569 public int Update (DataRow[] dataRows)
571 if (dataRows == null)
\r
572 throw new ArgumentNullException("dataRows");
\r
574 if (dataRows.Length == 0)
\r
577 if (dataRows[0] == null)
\r
578 throw new ArgumentException("dataRows[0].");
\r
580 DataTable table = dataRows[0].Table;
\r
582 throw new ArgumentException("table is null reference.");
\r
584 // all rows must be in the same table
\r
585 for (int i = 0; i < dataRows.Length; i++)
\r
587 if (dataRows[i] == null)
\r
588 throw new ArgumentException("dataRows[" + i + "].");
\r
589 if (dataRows[i].Table != table)
\r
590 throw new ArgumentException(
\r
593 + "] is from a different DataTable than DataRow[0].");
\r
596 // get table mapping for this rows
597 DataTableMapping tableMapping = TableMappings.GetByDataSetTable(table.TableName);
\r
598 if (tableMapping == null)
\r
600 tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction(
\r
604 MissingMappingAction);
\r
605 if (tableMapping == null)
\r
607 new DataTableMapping(
\r
612 DataRow[] copy = new DataRow [dataRows.Length];
613 Array.Copy(dataRows, 0, copy, 0, dataRows.Length);
614 return Update(copy, tableMapping);
617 public override int Update (DataSet dataSet)
619 return Update (dataSet, DefaultSourceTableName);
622 public int Update (DataTable dataTable)
624 int index = TableMappings.IndexOfDataSetTable (dataTable.TableName);
626 throw new ArgumentException ();
627 return Update (dataTable, TableMappings [index]);
630 private int Update (DataTable dataTable, DataTableMapping tableMapping)
632 DataRow[] rows = new DataRow [dataTable.Rows.Count];
633 dataTable.Rows.CopyTo (rows, 0);
634 return Update (rows, tableMapping);
638 protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping)
642 foreach (DataRow row in dataRows) {
643 StatementType statementType = StatementType.Update;
644 IDbCommand command = null;
645 string commandName = String.Empty;
646 bool useCommandBuilder = false;
648 switch (row.RowState) {
649 case DataRowState.Added:
650 statementType = StatementType.Insert;
651 command = InsertCommand;
652 commandName = "Insert";
654 case DataRowState.Deleted:
655 statementType = StatementType.Delete;
656 command = DeleteCommand;
657 commandName = "Delete";
659 case DataRowState.Modified:
660 statementType = StatementType.Update;
661 command = UpdateCommand;
662 commandName = "Update";
664 case DataRowState.Unchanged:
666 case DataRowState.Detached:
667 throw new NotImplementedException ();
671 useCommandBuilder = true;
673 RowUpdatingEventArgs args = CreateRowUpdatingEvent (row, command, statementType, tableMapping);
674 OnRowUpdating (args);
676 if (args.Status == UpdateStatus.ErrorsOccurred)
679 if (command == null && args.Command != null)
680 command = args.Command;
681 else if (command == null)
682 throw new InvalidOperationException (String.Format ("Update requires a valid {0}Command when passed a DataRow collection with modified rows.", commandName));
684 if (!useCommandBuilder) {
685 DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings;
687 foreach (IDataParameter parameter in command.Parameters) {
688 string dsColumnName = parameter.SourceColumn;
689 if (columnMappings.Contains(parameter.SourceColumn))
690 dsColumnName = columnMappings [parameter.SourceColumn].DataSetColumn;
691 DataRowVersion rowVersion = DataRowVersion.Default;
693 // Parameter version is ignored for non-update commands
694 if (statementType == StatementType.Update)
695 rowVersion = parameter.SourceVersion;
696 if (statementType == StatementType.Delete)
697 rowVersion = DataRowVersion.Original;
699 parameter.Value = row [dsColumnName, rowVersion];
703 CommandBehavior commandBehavior = CommandBehavior.Default;
704 if (command.Connection.State == ConnectionState.Closed)
706 command.Connection.Open ();
707 commandBehavior |= CommandBehavior.CloseConnection;
710 IDataReader reader = null;
713 // use ExecuteReader because we want to use the commandbehavior parameter.
714 // so the connection will be closed if needed.
715 reader = command.ExecuteReader (commandBehavior);
716 int tmp = reader.RecordsAffected;
717 // if the execute does not effect any rows we throw an exception.
719 throw new DBConcurrencyException("Concurrency violation: the " + commandName +"Command affected 0 records.");
721 OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, tableMapping));
722 row.AcceptChanges ();
726 if (ContinueUpdateOnError)
727 row.RowError = e.Message;// do somthing with the error
741 public int Update (DataSet dataSet, string sourceTable)
743 MissingMappingAction mappingAction = MissingMappingAction;
744 if (mappingAction == MissingMappingAction.Ignore)
745 mappingAction = MissingMappingAction.Error;
746 DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction);
748 DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable];
749 if (dataTable == null)
750 throw new ArgumentException ("sourceTable");
752 return Update (dataTable, tableMapping);
755 #if ONLY_1_0 || ONLY_1_1
756 protected virtual void OnFillError (FillErrorEventArgs value)
758 if (FillError != null)
759 FillError (this, value);
763 protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
764 protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
766 #endregion // Methods