[Mono-list] System.Data.Common.DbDataAdapter buglet...
Mark Easton
measton@tebiki.co.uk
Tue, 26 Aug 2003 08:19:12 +0100
This is a multi-part message in MIME format.
------=_NextPart_000_0010_01C36BAA.BB480EE0
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
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
______________________________________
mark.easton@blinksoftware.co.uk
+44(0)20 8406 3641
+44(0)77 1167 5960
------=_NextPart_000_0010_01C36BAA.BB480EE0
Content-Type: text/plain;
name="DbDataAdapter.cs"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="DbDataAdapter.cs"
//
// 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 =3D "Table";
const string DefaultSourceColumnName =3D "Column";
#endregion // Fields
=09
#region Constructors
protected DbDataAdapter()=20
{
}
#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
=09
#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 =3D new FillErrorEventArgs (dataTable, =
values);
args.Errors =3D e;
args.Continue =3D false;
return args;
}
protected override void Dispose (bool disposing)
{
if (disposing) {
((IDbDataAdapter) this).SelectCommand =3D null;
((IDbDataAdapter) this).InsertCommand =3D null;
((IDbDataAdapter) this).UpdateCommand =3D null;
((IDbDataAdapter) this).DeleteCommand =3D null;
}
}
public override int Fill (DataSet dataSet)
{
return Fill (dataSet, 0, 0, DefaultSourceTableName, SelectCommand, =
CommandBehavior.Default);
}
public int Fill (DataTable dataTable)=20
{
if (dataTable =3D=3D null)
throw new NullReferenceException ();
return Fill (dataTable, SelectCommand, CommandBehavior.Default);
}
public int Fill (DataSet dataSet, string srcTable)=20
{
return Fill (dataSet, 0, 0, srcTable, SelectCommand, =
CommandBehavior.Default);
}
protected virtual int Fill (DataTable dataTable, IDataReader =
dataReader)=20
{
int count =3D 0;
bool doContinue =3D true;
if (dataReader.FieldCount =3D=3D 0) {
dataReader.Close ();
return 0;
}
object[] itemArray =3D 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 +=3D 1;
}
catch (Exception e) {
FillErrorEventArgs args =3D CreateFillErrorEvent (dataTable, =
itemArray, e);
OnFillError (args);
doContinue =3D args.Continue;
}
}
dataReader.Close ();
return count;
}
protected virtual int Fill (DataTable dataTable, IDbCommand command, =
CommandBehavior behavior)=20
{
return Fill (dataTable, command.ExecuteReader (behavior));
}
public int Fill (DataSet dataSet, int startRecord, int maxRecords, =
string srcTable)=20
{
return this.Fill (dataSet, startRecord, maxRecords, srcTable, =
SelectCommand, CommandBehavior.Default);
}
protected virtual int Fill (DataSet dataSet, string srcTable, =
IDataReader dataReader, int startRecord, int maxRecords)=20
{
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 =3D=3D 0) {
dataReader.Close ();
return 0;
}
DataTable dataTable;
int resultIndex =3D 0;
int count =3D 0;
bool doContinue =3D true;
string tableName =3D srcTable;
object[] itemArray =3D new object [dataReader.FieldCount];
do {
dataTable =3D new DataTable ();
SetupSchema (SchemaType.Mapped, tableName, dataTable);
if (dataSet.Tables.Contains (dataTable.TableName))
dataTable =3D dataSet.Tables [tableName];
BuildSchema (dataReader, dataTable, SchemaType.Mapped);
for (int i =3D 0; i < startRecord; i +=3D 1)
dataReader.Read ();
while (doContinue && dataReader.Read () && !(maxRecords > 0 && count =
>=3D maxRecords)) {
dataReader.GetValues (itemArray);
try {
dataTable.BeginLoadData ();
dataTable.LoadDataRow (itemArray, AcceptChangesDuringFill);
dataTable.EndLoadData ();
count +=3D 1;
}
catch (Exception e) {
FillErrorEventArgs args =3D CreateFillErrorEvent (dataTable, =
itemArray, e);
OnFillError (args);
doContinue =3D args.Continue;
}
}
dataSet.Tables.Add (dataTable);
tableName =3D String.Format ("{0}{1}", =
srcTable, ++resultIndex);
startRecord =3D 0;
maxRecords =3D 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)=20
{
CommandBehavior commandBehavior =3D behavior;
if (command.Connection.State =3D=3D ConnectionState.Closed) {
command.Connection.Open ();
commandBehavior |=3D CommandBehavior.CloseConnection;
}
return Fill (dataSet, srcTable, command.ExecuteReader =
(commandBehavior), startRecord, maxRecords);
}
public override DataTable[] FillSchema (DataSet dataSet, SchemaType =
schemaType)=20
{
return FillSchema (dataSet, schemaType, SelectCommand, =
DefaultSourceTableName, CommandBehavior.Default);
}
public DataTable FillSchema (DataTable dataTable, SchemaType =
schemaType)=20
{
return FillSchema (dataTable, schemaType, SelectCommand, =
CommandBehavior.Default);
}
public DataTable[] FillSchema (DataSet dataSet, SchemaType schemaType, =
string srcTable)=20
{
return FillSchema (dataSet, schemaType, SelectCommand, srcTable, =
CommandBehavior.Default);
}
[MonoTODO ("Verify")]
protected virtual DataTable FillSchema (DataTable dataTable, =
SchemaType schemaType, IDbCommand command, CommandBehavior behavior)=20
{
DataTable table;
behavior |=3D CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
if (command.Connection.State =3D=3D ConnectionState.Closed) {
command.Connection.Open ();
behavior |=3D CommandBehavior.CloseConnection;
}
IDataReader reader =3D command.ExecuteReader (behavior);
table =3D 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)=20
{
behavior |=3D CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo;
if (command.Connection.State =3D=3D ConnectionState.Closed) {
command.Connection.Open ();
behavior |=3D CommandBehavior.CloseConnection;
}
IDataReader reader =3D command.ExecuteReader (behavior);
ArrayList output =3D new ArrayList ();
string tableName =3D srcTable;
int index =3D 0;
do {
DataTable table =3D new DataTable ();
SetupSchema (schemaType, tableName, table);
if (dataSet.Tables.Contains (table.TableName))
table =3D dataSet.Tables [table.TableName];=09
else
dataSet.Tables.Add (table);
BuildSchema (reader, table, schemaType);
output.Add (table);
tableName =3D 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 =3D null;
if (schemaType =3D=3D SchemaType.Mapped)=20
tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
sourceTableName, "", MissingMappingAction.Ignore);
if (tableMapping !=3D null)
table.TableName =3D tableMapping.DataSetTable;
else
table.TableName =3D sourceTableName;
}
[EditorBrowsable (EditorBrowsableState.Advanced)]
public override IDataParameter[] GetFillParameters ()=20
{
object[] parameters =3D 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 =3D new ArrayList ();
ArrayList sourceColumns =3D 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 =3D DefaultSourceColumnName;
else=20
sourceColumnName =3D (string) schemaRow ["ColumnName"];
string realSourceColumnName =3D sourceColumnName;
for (int i =3D 1; sourceColumns.Contains (realSourceColumnName); i =
+=3D 1)=20
realSourceColumnName =3D String.Format ("{0}{1}", sourceColumnName, =
i);
sourceColumns.Add(realSourceColumnName);
// generate DataSetColumnName from DataTableMapping, if any
string dsColumnName =3D realSourceColumnName;
DataTableMapping tableMapping =3D null;
if (schemaType =3D=3D SchemaType.Mapped)
tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
table.TableName, table.TableName, MissingMappingAction.Ignore);=20
if (tableMapping !=3D null) {
table.TableName =3D tableMapping.DataSetTable;
// check to see if the column mapping exists
if (tableMapping.ColumnMappings.Contains (dsColumnName)) {
dsColumnName =3D tableMapping.ColumnMappings =
[realSourceColumnName].DataSetColumn;
} else {
if (MissingSchemaAction =3D=3D 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]);=09
}
if (MissingSchemaAction =3D=3D MissingSchemaAction.AddWithKey && =
primaryKey.Count > 0){
table.PrimaryKey =3D =
(DataColumn[])primaryKey.ToArray(typeof(DataColumn));
}
}
[MonoTODO]
object ICloneable.Clone ()
{
throw new NotImplementedException ();
}
[MonoTODO]
public int Update (DataRow[] dataRows)=20
{
throw new NotImplementedException (); // FIXME: Which mapping?
}
public override int Update (DataSet dataSet)=20
{
return Update (dataSet, DefaultSourceTableName);
}
public int Update (DataTable dataTable)=20
{
int index =3D TableMappings.IndexOfDataSetTable =
(dataTable.TableName);
if (index < 0)
throw new ArgumentException ();
return Update (dataTable, TableMappings [index]);
}
private int Update (DataTable dataTable, DataTableMapping =
tableMapping)
{
DataRow[] rows =3D new DataRow [dataTable.Rows.Count];
dataTable.Rows.CopyTo (rows, 0);
return Update (rows, tableMapping);
}
[MonoTODO]
protected virtual int Update (DataRow[] dataRows, DataTableMapping =
tableMapping)=20
{
int updateCount =3D 0;
foreach (DataRow row in dataRows) {
StatementType statementType =3D StatementType.Update;
IDbCommand command =3D null;
string commandName =3D String.Empty;
bool useCommandBuilder =3D false;
switch (row.RowState) {
case DataRowState.Added:
statementType =3D StatementType.Insert;
command =3D InsertCommand;
commandName =3D "Insert";
break;
case DataRowState.Deleted:
statementType =3D StatementType.Delete;
command =3D DeleteCommand;
commandName =3D "Delete";
break;
case DataRowState.Modified:
statementType =3D StatementType.Update;
command =3D UpdateCommand;
commandName =3D "Update";
break;
case DataRowState.Unchanged:
continue;
case DataRowState.Detached:
throw new NotImplementedException ();
}
if (command =3D=3D null)
useCommandBuilder =3D true;
RowUpdatingEventArgs args =3D CreateRowUpdatingEvent (row, command, =
statementType, tableMapping);
OnRowUpdating (args);
if (args.Status =3D=3D UpdateStatus.ErrorsOccurred)
throw (args.Errors);
if (command =3D=3D null && args.Command !=3D null)
command =3D args.Command;
else if (command =3D=3D 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 =3D =
tableMapping.ColumnMappings;
foreach (IDataParameter parameter in command.Parameters) {
string dsColumnName =3D parameter.SourceColumn;
DataColumnMapping mapping =3D columnMappings =
[parameter.SourceColumn];
if (mapping !=3D null) dsColumnName =3D mapping.DataSetColumn;
DataRowVersion rowVersion =3D DataRowVersion.Default;
// Parameter version is ignored for non-update commands
if (statementType =3D=3D StatementType.Update)=20
rowVersion =3D parameter.SourceVersion;
if (statementType =3D=3D StatementType.Delete)=20
rowVersion =3D DataRowVersion.Original;
parameter.Value =3D row [dsColumnName, rowVersion];
}
row.AcceptChanges ();
}
updateCount +=3D command.ExecuteNonQuery ();
OnRowUpdated (CreateRowUpdatedEvent (row, command, statementType, =
tableMapping));
}
return updateCount;
}
public int Update (DataSet dataSet, string sourceTable)=20
{
MissingMappingAction mappingAction =3D MissingMappingAction;
if (mappingAction =3D=3D MissingMappingAction.Ignore)
mappingAction =3D MissingMappingAction.Error;
DataTableMapping tableMapping =3D =
DataTableMappingCollection.GetTableMappingBySchemaAction (TableMappings, =
sourceTable, sourceTable, mappingAction);
DataTable dataTable =3D dataSet.Tables[tableMapping.DataSetTable];
if (dataTable =3D=3D null)
throw new ArgumentException ("sourceTable");
return Update (dataTable, tableMapping);
}
protected virtual void OnFillError (FillErrorEventArgs value)=20
{
if (FillError !=3D null)
FillError (this, value);
}
protected abstract void OnRowUpdated (RowUpdatedEventArgs value);
protected abstract void OnRowUpdating (RowUpdatingEventArgs value);
=09
#endregion // Methods
}
}
------=_NextPart_000_0010_01C36BAA.BB480EE0--