Having just found a ridiculously simple buglet in the DbDataAdapter.cs
file's 'BuildSchema' method, I thought I'd send some details through for
perusal. I would have included a test but haven't had a chance yet to
work out how the common data methods should be tested in a provider
agnostic fashion.

A description of the buglet follows:

The last call to 'ToArray' in 'BuildSchema' doesn't specify a type and
can be fixed by adding 'typeof(DataColumn)' as a parameter, as follows: 

Current Implementation:

if (MissingSchemaAction == MissingSchemaAction.AddWithKey &&
primaryKey.Count > 0){
                                table.PrimaryKey =
(DataColumn[])primaryKey.ToArray();
                        }

Fixed Implementation:

if (MissingSchemaAction == MissingSchemaAction.AddWithKey &&
primaryKey.Count > 0){
                                table.PrimaryKey =
(DataColumn[])primaryKey.ToArray(typeof(DataColumn));
                        }

Hope this is of help

Mark
______________________________________

[EMAIL PROTECTED]
+44(0)20 8406 3641
+44(0)77 1167 5960

//
// System.Data.Common.DbDataAdapter.cs
//
// Author:
//   Rodrigo Moya ([EMAIL PROTECTED])
//   Tim Coleman ([EMAIL PROTECTED])
//
// (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
        }
}

Reply via email to