// // System.Data.Common.DbDataAdapter.cs // // Author: // Rodrigo Moya (rodrigo@ximian.com) // Tim Coleman (tim@timcoleman.com) // // (C) Ximian, Inc // Copyright (C) 2002 Tim Coleman // using System; using System.Collections; using System.ComponentModel; using System.Data; namespace System.Data.Common { public abstract class DbDataAdapter : DataAdapter, ICloneable { #region Fields public const string DefaultSourceTableName = "Table"; const string DefaultSourceColumnName = "Column"; #endregion // Fields #region Constructors protected DbDataAdapter() { } #endregion // Fields #region Properties IDbCommand DeleteCommand { get { return ((IDbDataAdapter) this).DeleteCommand; } } IDbCommand InsertCommand { get { return ((IDbDataAdapter) this).InsertCommand; } } IDbCommand SelectCommand { get { return ((IDbDataAdapter) this).SelectCommand; } } IDbCommand UpdateCommand { get { return ((IDbDataAdapter) this).UpdateCommand; } } #endregion // Properties #region Events [DataCategory ("Fill")] [DataSysDescription ("Event triggered when a recoverable error occurs during Fill.")] public event FillErrorEventHandler FillError; #endregion // Events #region Methods protected abstract RowUpdatedEventArgs CreateRowUpdatedEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping); protected abstract RowUpdatingEventArgs CreateRowUpdatingEvent (DataRow dataRow, IDbCommand command, StatementType statementType, DataTableMapping tableMapping); private FillErrorEventArgs CreateFillErrorEvent (DataTable dataTable, object[] values, Exception e) { FillErrorEventArgs args = new FillErrorEventArgs (dataTable, values); args.Errors = e; args.Continue = false; return args; } protected override void Dispose (bool disposing) { if (disposing) { ((IDbDataAdapter) this).SelectCommand = null; ((IDbDataAdapter) this).InsertCommand = null; ((IDbDataAdapter) this).UpdateCommand = null; ((IDbDataAdapter) this).DeleteCommand = null; } } public override int Fill (DataSet dataSet) { return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, CommandBehavior.Default); } public int Fill (DataTable dataTable) { if (dataTable == null) throw new NullReferenceException (); return Fill (dataTable, SelectCommand, CommandBehavior.Default); } public int Fill (DataSet dataSet, string srcTable) { return Fill (dataSet, 0, 0, srcTable, SelectCommand, CommandBehavior.Default); } protected virtual int Fill (DataTable dataTable, IDataReader dataReader) { int count = 0; bool doContinue = true; if (dataReader.FieldCount == 0) { dataReader.Close (); return 0; } object[] itemArray = new object [dataReader.FieldCount]; SetupSchema (SchemaType.Mapped, dataTable.TableName, dataTable); // FIXME BuildSchema (dataReader, dataTable, SchemaType.Mapped); while (doContinue && dataReader.Read ()) { dataReader.GetValues (itemArray); try { dataTable.BeginLoadData (); dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill); dataTable.EndLoadData (); count += 1; } catch (Exception e) { FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e); OnFillError (args); doContinue = args.Continue; } } dataReader.Close (); return count; } protected virtual int Fill (DataTable dataTable, IDbCommand command, CommandBehavior behavior) { return Fill (dataTable, command.ExecuteReader (behavior)); } public int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable) { return this.Fill (dataSet, startRecord, maxRecords, srcTable, SelectCommand, CommandBehavior.Default); } protected virtual int Fill (DataSet dataSet, string srcTable, IDataReader dataReader, int startRecord, int maxRecords) { if (startRecord < 0) throw new ArgumentException ("The startRecord parameter was less than 0."); if (maxRecords < 0) throw new ArgumentException ("The maxRecords parameter was less than 0."); if (dataReader.FieldCount == 0) { dataReader.Close (); return 0; } DataTable dataTable; int resultIndex = 0; int count = 0; bool doContinue = true; string tableName = srcTable; object[] itemArray = new object [dataReader.FieldCount]; do { dataTable = new DataTable (); SetupSchema (SchemaType.Mapped, tableName, dataTable); if (dataSet.Tables.Contains (dataTable.TableName)) dataTable = dataSet.Tables [tableName]; BuildSchema (dataReader, dataTable, SchemaType.Mapped); for (int i = 0; i < startRecord; i += 1) dataReader.Read (); while (doContinue && dataReader.Read () && !(maxRecords > 0 && count >= maxRecords)) { dataReader.GetValues (itemArray); try { dataTable.BeginLoadData (); dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill); dataTable.EndLoadData (); count += 1; } catch (Exception e) { FillErrorEventArgs args = CreateFillErrorEvent (dataTable, itemArray, e); OnFillError (args); doContinue = args.Continue; } } dataSet.Tables.Add (dataTable); tableName = String.Format ("{0}{1}", srcTable, ++resultIndex); startRecord = 0; maxRecords = 0; } while (doContinue && dataReader.NextResult ()); dataReader.Close (); return count; } protected virtual int Fill (DataSet dataSet, int startRecord, int maxRecords, string srcTable, IDbCommand command, CommandBehavior behavior) { CommandBehavior commandBehavior = behavior; if (command.Connection.State == ConnectionState.Closed) { command.Connection.Open (); commandBehavior |= CommandBehavior.CloseConnection; } return Fill (dataSet, srcTable, command.ExecuteReader (commandBehavior), startRecord, maxRecords); } public override DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType) { return FillSchema (dataSet, schemaType, SelectCommand, DefaultSourceTableName, CommandBehavior.Default); } public DataTable FillSchema (DataTable dataTable, SchemaType schemaType) { return FillSchema (dataTable, schemaType, SelectCommand, CommandBehavior.Default); } public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, string srcTable) { return FillSchema (dataSet, schemaType, SelectCommand, srcTable, CommandBehavior.Default); } [MonoTODO ("Verify")] protected virtual DataTable FillSchema (DataTable dataTable, SchemaType schemaType, IDbCommand command, CommandBehavior behavior) { DataTable table; behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo; if (command.Connection.State == ConnectionState.Closed) { command.Connection.Open (); behavior |= CommandBehavior.CloseConnection; } IDataReader reader = command.ExecuteReader (behavior); table = new DataTable (); SetupSchema (schemaType, DefaultSourceTableName, table); BuildSchema (reader, table, schemaType); reader.Close (); return table; } [MonoTODO ("Verify")] protected virtual DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, IDbCommand command, string srcTable, CommandBehavior behavior) { behavior |= CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo; if (command.Connection.State == ConnectionState.Closed) { command.Connection.Open (); behavior |= CommandBehavior.CloseConnection; } IDataReader reader = command.ExecuteReader (behavior); ArrayList output = new ArrayList (); string tableName = srcTable; int index = 0; do { DataTable table = new DataTable (); SetupSchema (schemaType, tableName, table); if (dataSet.Tables.Contains (table.TableName)) table = dataSet.Tables [table.TableName]; else dataSet.Tables.Add (table); BuildSchema (reader, table, schemaType); output.Add (table); tableName = String.Format ("{0}{1}", srcTable, ++index); } while (reader.NextResult ()); reader.Close (); return (DataTable[]) output.ToArray (typeof (DataTable)); } private void SetupSchema (SchemaType schemaType, string sourceTableName, DataTable table) { DataTableMapping tableMapping = null; if (schemaType == SchemaType.Mapped) tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTableName, "", MissingMappingAction.Ignore); if (tableMapping != null) table.TableName = tableMapping.DataSetTable; else table.TableName = sourceTableName; } [EditorBrowsable (EditorBrowsableState.Advanced)] public override IDataParameter[] GetFillParameters () { object[] parameters = new object [SelectCommand.Parameters.Count]; SelectCommand.Parameters.CopyTo (parameters, 0); return (IDataParameter[]) parameters; } [MonoTODO ("Test")] private void BuildSchema (IDataReader reader, DataTable table, SchemaType schemaType) { ArrayList primaryKey = new ArrayList (); ArrayList sourceColumns = new ArrayList (); foreach (DataRow schemaRow in reader.GetSchemaTable ().Rows) { // generate a unique column name in the source table. string sourceColumnName; if (schemaRow ["ColumnName"].Equals (DBNull.Value)) sourceColumnName = DefaultSourceColumnName; else sourceColumnName = (string) schemaRow ["ColumnName"]; string realSourceColumnName = sourceColumnName; for (int i = 1; sourceColumns.Contains (realSourceColumnName); i += 1) realSourceColumnName = String.Format ("{0}{1}", sourceColumnName, i); sourceColumns.Add(realSourceColumnName); // generate DataSetColumnName from DataTableMapping, if any string dsColumnName = realSourceColumnName; DataTableMapping tableMapping = null; if (schemaType == SchemaType.Mapped) tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, table.TableName, table.TableName, MissingMappingAction.Ignore); if (tableMapping != null) { table.TableName = tableMapping.DataSetTable; // check to see if the column mapping exists if (tableMapping.ColumnMappings.Contains (dsColumnName)) { dsColumnName = tableMapping.ColumnMappings [realSourceColumnName].DataSetColumn; } else { if (MissingSchemaAction == MissingSchemaAction.Error) throw new SystemException (); tableMapping.ColumnMappings.Add (sourceColumnName, dsColumnName); } if (!TableMappings.Contains (tableMapping)) TableMappings.Add (tableMapping); } if (!table.Columns.Contains(dsColumnName)) table.Columns.Add (dsColumnName, (Type) schemaRow ["DataType"]); if (!schemaRow["IsKey"].Equals (DBNull.Value)) if ((bool) (schemaRow ["IsKey"])) primaryKey.Add (table.Columns [dsColumnName]); } if (MissingSchemaAction == MissingSchemaAction.AddWithKey && primaryKey.Count > 0) table.PrimaryKey = (DataColumn[])(primaryKey.ToArray(typeof (DataColumn))); } [MonoTODO] object ICloneable.Clone () { throw new NotImplementedException (); } [MonoTODO] public int Update (DataRow[] dataRows) { throw new NotImplementedException (); // FIXME: Which mapping? } public override int Update (DataSet dataSet) { return Update (dataSet, DefaultSourceTableName); } public int Update (DataTable dataTable) { int index = TableMappings.IndexOfDataSetTable (dataTable.TableName); if (index < 0) throw new ArgumentException (); return Update (dataTable, TableMappings [index]); } private int Update (DataTable dataTable, DataTableMapping tableMapping) { DataRow[] rows = new DataRow [dataTable.Rows.Count]; dataTable.Rows.CopyTo (rows, 0); return Update (rows, tableMapping); } [MonoTODO] protected virtual int Update (DataRow[] dataRows, DataTableMapping tableMapping) { int updateCount = 0; foreach (DataRow row in dataRows) { StatementType statementType = StatementType.Update; IDbCommand command = null; string commandName = String.Empty; bool useCommandBuilder = false; switch (row.RowState) { case DataRowState.Added: statementType = StatementType.Insert; command = InsertCommand; commandName = "Insert"; break; case DataRowState.Deleted: statementType = StatementType.Delete; command = DeleteCommand; commandName = "Delete"; break; case DataRowState.Modified: statementType = StatementType.Update; command = UpdateCommand; commandName = "Update"; break; case DataRowState.Unchanged: continue; case DataRowState.Detached: throw new NotImplementedException (); } if (command == null) useCommandBuilder = true; RowUpdatingEventArgs args = CreateRowUpdatingEvent (row, command, statementType, tableMapping); OnRowUpdating (args); if (args.Status == UpdateStatus.ErrorsOccurred) throw (args.Errors); if (command == null && args.Command != null) command = args.Command; else if (command == null) throw new InvalidOperationException (String.Format ("Update requires a valid {0}Command when passed a DataRow collection with modified rows.", commandName)); if (!useCommandBuilder) { DataColumnMappingCollection columnMappings = tableMapping.ColumnMappings; foreach (IDataParameter parameter in command.Parameters) { string dsColumnName = parameter.SourceColumn; DataColumnMapping mapping = columnMappings [parameter.SourceColumn]; if (mapping != null) dsColumnName = mapping.DataSetColumn; DataRowVersion rowVersion = DataRowVersion.Default; // Parameter version is ignored for non-update commands if (statementType == StatementType.Update) rowVersion = parameter.SourceVersion; if (statementType == StatementType.Delete) rowVersion = DataRowVersion.Original; parameter.Value = row [dsColumnName, rowVersion]; } row.AcceptChanges (); } updateCount += command.ExecuteNonQuery (); OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, tableMapping)); } return updateCount; } public int Update (DataSet dataSet, string sourceTable) { MissingMappingAction mappingAction = MissingMappingAction; if (mappingAction == MissingMappingAction.Ignore) mappingAction = MissingMappingAction.Error; DataTableMapping tableMapping = DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, sourceTable, sourceTable, mappingAction); DataTable dataTable = dataSet.Tables[tableMapping.DataSetTable]; if (dataTable == null) throw new ArgumentException ("sourceTable"); return Update (dataTable, tableMapping); } protected virtual void OnFillError (FillErrorEventArgs value) { if (FillError != null) FillError (this, value); } protected abstract void OnRowUpdated (RowUpdatedEventArgs value); protected abstract void OnRowUpdating (RowUpdatingEventArgs value); #endregion // Methods } }