SqlDataAdapter inherits IDbDataAdapter but does not implement all it's methods!?

M

Michael Lang

This is not a question about ADO.NET, but about implementing .NET
interfaces.

SqlDataAdapter inherits DbDataAdapter and implements IDbDataAdapter
DbDataAdapter inherits DataAdapter and implements ICloneable
DataAdapter inherits Component and implements IDataAdapter
SqlCommand inherits Component and implements IDbCommand and ICloneable

Note: DbDataAdapter does NOT implement IDbDataAdapter
and DataAdapter does NOT implement IDataAdapter

IDbDataAdapter contains:
IDbCommand DeleteCommand{get;set;}
IDbCommand InsertCommand{get;set;}
IDbCommand SelectCommand{get;set;}
IDbCommand UpdateCommand{get;set;}

SqlDataAdapter contains (partial):
SqlCommand DeleteCommand{get;set;}
SqlCommand InsertCommand{get;set;}
SqlCommand SelectCommand{get;set;}
SqlCommand UpdateCommand{get;set;}

I am trying to implement IDbDataAdapter containing (partial):
public class GenDataAdapter: DbDataAdapter, IDbDataAdapter
{ //...Note: same inheritance as SqlDataAdapter... Except 'Component'
GenCommand DeleteCommand{get;set;}
GenCommand InsertCommand{get;set;}
GenCommand SelectCommand{get;set;}
GenCommand UpdateCommand{get;set;}
}
public class GenCommand: IDbCommand{...} // implements IDbCommand...

I get these compile error (similar for each of commands):
'GenDB.Data.GenDataAdapter' does not implement interface member
'System.Data.IDbDataAdapter.UpdateCommand'.
'GenDB.Data.GenDataAdapter.UpdateCommand' is either static, not public, or
has the wrong return type.

Why is this? How does the SqlDataAdapter get around not having each of the
commands return type IDbCommand? Is there some keyword I need to define a
more specific type for the return type than the IDbDataAdapter interface
defines?
 
D

David Browne

Michael Lang said:
This is not a question about ADO.NET, but about implementing .NET
interfaces.


I am trying to implement IDbDataAdapter containing (partial):
public class GenDataAdapter: IDbDataAdapter
{ > GenCommand DeleteCommand{get;set;}
GenCommand InsertCommand{get;set;}
GenCommand SelectCommand{get;set;}
GenCommand UpdateCommand{get;set;}
}
public class GenCommand: IDbCommand{...} // implements IDbCommand...

I don't think yoru GenDataAdapter you want to inherit any class.
Just implement the IDbDataAdapter interface, and pass through the
implementation
to a provider-specific DataAdapter.

Like the example at the end.

Then you can add functionality to GenDBAdapter, like events.
And you can use provider-specific subtypes of GenDbAdapter to solve hard
problems with different adapters.

David

class GenDbAdapter : IDbDataAdapter
{
private IDbDataAdapter da;
public GenDbAdapter(IDbDataAdapter da)
{
this.da = da;
}


#region Implementation of IDbDataAdapter
public IDbCommand UpdateCommand
{
get
{
return da.UpdateCommand;
}
set
{
da.UpdateCommand = value;
}
}

public IDbCommand SelectCommand
{
get
{
return da.SelectCommand;
}
set
{
da.SelectCommand = value;
}
}

public IDbCommand DeleteCommand
{
get
{
return da.DeleteCommand;
}
set
{
da.DeleteCommand = value;
}
}

public IDbCommand InsertCommand
{
get
{
return da.InsertCommand;
}
set
{
da.InsertCommand = value;
}
}
#endregion

#region Implementation of IDataAdapter
public int Fill(DataSet dataSet)
{
return da.Fill(dataSet);
}

public IDataParameter[] GetFillParameters()
{
return da.GetFillParameters();
}

public DataTable[] FillSchema(DataSet dataSet, SchemaType schemaType)
{
return da.FillSchema(dataSet,schemaType);
}

public int Update(DataSet dataSet)
{
return da.Update(dataSet);
}

public ITableMappingCollection TableMappings
{
get
{
return da.TableMappings;
}
}

public MissingSchemaAction MissingSchemaAction
{
get
{
return da.MissingSchemaAction;
}
set
{
da.MissingSchemaAction = value;
}
}

public MissingMappingAction MissingMappingAction
{
get
{
return da.MissingMappingAction;
}
set
{
da.MissingMappingAction=value;
}
}
#endregion
}

David
 
J

Jay B. Harlow [MVP - Outlook]

Michael,
This is considered overloading. However, the return type can't be the only
difference between versions of overloaded methods. So I can't declare the
following 2 methods in the same class:
Actually this is considered 'Explicit interface member implementation'. For
details see:
http://msdn.microsoft.com/library/d.../en-us/csspec/html/vclrfcsharpspec_13_4_1.asp

So yes you can declare both versions as Jeremy demonstrated!

Notice that the first DeleteCommand is public, while the second
DeleteCommand has no visibility and the name is qualified with the name of
the interface. When using Explicit interface member implementation you are
effectively hiding the method from the class. The only way to use this
method is via casting the object to the interface, and calling the method
via the interface.

Explicit interface member implementation is how the SqlDataAdapter does
it... VB.NET uses the Implements clause on the method, so there you can
simply declare the method as private and change the name, to get the same
effect.

Hope this helps
Jay
 
J

Jeremy Wilde

I'm certain the technique works, have used it myself. I first saw it in used
in the collection code generated by the .NET CollectionGen Tool I downloaded
from http://www.sellsbrothers.com. I would guess that either you have it
declared in another section of your code, or one of your base classes also
implements it, in which case your public one would need to be declared as an
override.

If you were to access the SqlCommand through the IDbDataAdapter interface
those properties would automatically return them as IDbCommand objects and
you wouldn't have to perform the cast.

The hiding mechanism I outlined is what makes it possible for a class to
seemingly overload a property or method when the only difference is in the
return value. The ones tied to the interface by preceding the identifier
with the interface name are only visible when the object is being referenced
through a variable declared as the interface type.
 
M

Michael Lang

This is considered overloading. However, the return type can't be the only
difference between versions of overloaded methods. So I can't declare the
following 2 methods in the same class:

IDbCommand IDbDataAdapter.DeleteCommand{get;set;}
public GenCommand DeleteCommand{get;set;}

I get this error:
The class 'GenDB.Data.GenDataAdapter' already contains a definition for
'DeleteCommand'

BTW, When using the SqlDataAdapter.DeleteCommand property you can cast it
into an IDbCommand, because the SqlCommand implements the IDbCommand. My
GenCommand also implements IDbCommand.
 
M

Michael Lang

I had exactly that at one time. It does work. However, I have another
class called GenCommand, and so I wanted the commands (IE.
GenDataAdapter.SelectCommand) to return a "GenCommand".

If this is considered breaking the interface contract (to return a derived
type than interface defines) then how does the built in framework type
SqlDataAdapter get around that rule!!! It's unfair!

This is what I had the first time around:
================================================================
public class GenDataAdapter: IDbDataAdapter
{
private IDbDataAdapter _da;
internal GenDataAdapter(IDbDataAdapter da)
{
_da = da;
System.Type tda = _da.GetType();
if (tda == typeof(SqlDataAdapter))
{
SqlDataAdapter sqlda = (SqlDataAdapter)_da;
sqlda.RowUpdated += new SqlRowUpdatedEventHandler(
this.sql_RowUpdated_Handler);
sqlda.RowUpdating += new SqlRowUpdatingEventHandler(
this.sql_RowUpdating_Handler);
sqlda.FillError += new FillErrorEventHandler(
this.sql_FillError_Handler);
sqlda.Disposed += new System.EventHandler(
this.sql_Disposed_Handler);
}
else if (tda == typeof(OleDbDataAdapter))
{
OleDbDataAdapter oledbda = (OleDbDataAdapter)_da;
oledbda.RowUpdated += new OleDbRowUpdatedEventHandler(
this.oledb_RowUpdated_Handler);
oledbda.RowUpdating += new OleDbRowUpdatingEventHandler(
this.oledb_RowUpdating_Handler);
oledbda.FillError += new FillErrorEventHandler(
this.oledb_FillError_Handler);
oledbda.Disposed += new System.EventHandler(
this.oledb_Disposed_Handler);
}
else if (tda == typeof(OdbcDataAdapter))
{
OdbcDataAdapter odbcda = (OdbcDataAdapter)_da;
odbcda.RowUpdated += new OdbcRowUpdatedEventHandler(
this.odbc_RowUpdated_Handler);
odbcda.RowUpdating += new OdbcRowUpdatingEventHandler(
this.odbc_RowUpdating_Handler);
odbcda.FillError += new FillErrorEventHandler(
this.odbc_FillError_Handler);
odbcda.Disposed += new System.EventHandler(
this.odbc_Disposed_Handler);
}
else
{ //extension types... must inherit from RowUpdatedEventHandler
//TODO:
}
}
#region "events"
#region "Disposed"
public event EventHandler Disposed;
protected void OnDisposed(object sender, EventArgs e)
{
if (Disposed != null)
{
this.Disposed(sender, e);
}
}
private void sql_Disposed_Handler(object sender, EventArgs e)
{
OnDisposed(sender, e);
}
private void oledb_Disposed_Handler(object sender, EventArgs e)
{
OnDisposed(sender, e);
}
private void odbc_Disposed_Handler(object sender, EventArgs e)
{
OnDisposed(sender, e);
}
#endregion
#region "FillError"
... Similar to Disposed ...
#endregion
#region "RowUpdating"
public event RowUpdatingEventHandler RowUpdating;
protected void OnRowUpdating(object sender, RowUpdatingEventArgs e)
{
if (RowUpdating != null)
{
this.RowUpdating(sender, e);
}
}
private void sql_RowUpdating_Handler(object sender, SqlRowUpdatingEventArgs
e)
{
RowUpdatingEventArgs e2 = (RowUpdatingEventArgs)e;
OnRowUpdating(sender, e2);
}
private void oledb_RowUpdating_Handler(object sender,
OleDbRowUpdatingEventArgs e)
{
RowUpdatingEventArgs e2 = (RowUpdatingEventArgs)e;
OnRowUpdating(sender, e2);
}
private void odbc_RowUpdating_Handler(object sender,
OdbcRowUpdatingEventArgs e)
{
RowUpdatingEventArgs e2 = (RowUpdatingEventArgs)e;
OnRowUpdating(sender, e2);
}
#endregion
#region "RowUpdated"
...similar to RowUpdating
#endregion
#endregion
/// <summary>
/// TODO: convert to use GenCommand
/// </summary>
public IDbCommand SelectCommand
{
get{return _da.SelectCommand;}
set{_da.SelectCommand = value;}
}
/// <summary>
/// TODO: convert to use GenCommand
/// </summary>
public IDbCommand InsertCommand
{
get{return _da.InsertCommand;}
set{_da.InsertCommand = value;}
}
/// <summary>
/// TODO: convert to use GenCommand
/// </summary>
public IDbCommand UpdateCommand
{
get{return _da.UpdateCommand;}
set{_da.UpdateCommand = value;}
}
/// <summary>
/// TODO: convert to use GenCommand
/// </summary>
public IDbCommand DeleteCommand
{
get{return _da.DeleteCommand;}
set{_da.DeleteCommand = value;}
}
#region "simple delegation"
public IDbDataAdapter DataAdapter
{get{return _da;}}
public int Update(DataSet ds)
{return _da.Update(ds);}
public int Fill(DataSet ds)
{return _da.Fill(ds);}
public DataTable[] FillSchema(DataSet ds, SchemaType st)
{return _da.FillSchema(ds, st);}
public IDataParameter[] GetFillParameters()
{return _da.GetFillParameters();}
public ITableMappingCollection TableMappings
{
get{return _da.TableMappings;}
}
public MissingSchemaAction MissingSchemaAction
{
get{return _da.MissingSchemaAction;}
set{_da.MissingSchemaAction = value;}
}
public MissingMappingAction MissingMappingAction
{
get{return _da.MissingMappingAction;}
set{_da.MissingMappingAction = value;}
}
#region "object"
public override string ToString()
{return _da.ToString();}
public override bool Equals(object o)
{return _da.Equals(o);}
public override int GetHashCode()
{return _da.GetHashCode();}
#endregion
#endregion
}

================================================================
 
M

Michael Lang

AH. I'm not sure why it didn't compile before, but it compiles now. Not
sure if it works at runtime yet. I'll let you know if I have problems...
Here is my new version (changes, partial)...

IDbCommand IDbDataAdapter.SelectCommand
{
get{return _da.SelectCommand;}
set{_da.SelectCommand = value;}
}
public GenCommand SelectCommand
{
get{return new GenCommand(_da.SelectCommand);}
set{_da.SelectCommand = value.Command;}
}
public IDbDataAdapter DataAdapter
{
get{return _da;}
}

I think before I had...
IDbCommand SelectCommand{...}
instead of
IDbCommand IDbDataAdapter.SelectCommand{...}

Thanks for the help!
 
M

Michael Lang

My problem was in declaring the 2 similar methods only differing by return
type. The casting works fine. In my version that inherits from
DbDataAdapter I have what you are showing.

Problem solved in how I declared them... see other thread branch.

Thanks!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top