Context:
I'm working on a .NET C# application in Visual Studio.
I discovered DataSets to interact with my DataBase, so I use TableAdapters. Each Time I add a new TableAdapter associated with a table, the designer create several specific classes. If I create a TableApdapter for the Table 'myTable', the designer would create the class MyTableTableAdapter, inherited from global::system.Component, and the class MyTableDataTable, inherited from DataTable.
Problem:
I created a custom DataGridView that have the following attribute:
private System.ComponentModel.Component tableAdapter;
My component should be able to manipulate any TableAdapter created by the designer.
I will now simple the problem:
//Auto-generated by the designer
public partial class ATableAdapter : global::system.Component
{
public virtual ADataTable GetData()
{
ADataTable aDataTable = new ADataTable();
// Do things
return aDataTable;
}
}
//Auto-generated by the designer
public partial class BTableAdapter : global::system.Component
{
public virtual BDataTable GetData()
{
ADataTable bDataTable = new BDataTable();
// Do things
return bDataTable;
}
}
// My code
public class TableAdapterManager
{
private Component tableAdapter;
public TableAdapterManager(Component tableAdapter)
{
this.tableAdapter = tableAdapter;
}
// What I want to do
public DataTable GetData()
{
return tableAdapter.GetData();
}
}
The main problem emerge :
the class Component doesn't implement any definition for public DataTable GetData()
.
I need to get the DataTable returned from the method GetData(), no matter the type of TableAdapter. The thing is: I don't want to modify auto-generated code. I modified it once, and the designer overwrote it.
So far, I thought about Interfaces :
public interface IDataProvider
{
DataTable GetData();
}
As auto-generated classes are partial, I decided to code another part of each class:
public partial class ATableAdapter : IDataProvider
{
public DataTable GetData() // In respect to the interface
{
ADataTable aDataTable = new ADataTable();
// Do exact same things as in the original definition but with DataTable parent type
return adataTable;
}
}
public partial class BTableAdapter : IDataProvider
{
public DataTable GetData() // In respect to the interface
{
BDataTable bDataTable = new BDataTable();
// Do exact same things as in the original definition but with DataTable parent type
return bDataTable;
}
}
// My code
public class TableAdapterManager
{
private Component tableAdapter;
public TableAdapterManager(Component tableAdapter)
{
this.tableAdapter = tableAdapter;
}
// What I want to do
public DataTable GetData()
{
return ((IDataProvider)tableAdapter).GetData();
}
}
It seemed correct, but I got that error:
Error CS0111 Type 'ATableAdapter' already defines a member called 'GetData' with the same parameter types.
Error CS0111 Type 'BTableAdapter' already defines a member called 'GetData' with the same parameter types.
C:\Path\ProjectDataSet.Designer.cs
I thought the Interface approach was consistent but I don't see any other solution.
An easy solution would be to name the interface's method slightly different, but it would it wouldn't be as rigorous.
There are two possible quick solutions:
Simply change your hand-written partial
types such that the GetData()
methods are explicit implementations of IDataProvider
:
public partial class ATableAdapter : IDataProvider
{
DataTable IDataProvider.GetData() => this.GetData();
}
This works because:
this.GetData()
call-sites will always resolve to the public virtual BDataTable GetData()
methods in the codegen'd file (as this.
cannot be used to dereference explicit interface implementation members).this.GetData()
returns BDataTable
which derives from DataTable
which means it can be returned from DataTable IDataProvider.GetData()
as-is.This way, your TableAdapterManager
(which sounds rather Enterprise-y) code will work using your current return ((IDataProvider)tableAdapter).GetData();
statement.
Assuming you're using C# 8 (or 9?) or later, then extend your IDataProvider
with Default Implementation Members and Generic Type Parameter variance, like so:
public interface IDataProvider
{
public abstract DataTable GetData();
}
public interface IDataProvider<out TDataTable> : IDataProvider
where TDataTable : DataTable
{
public new abstract TDataTable GetData();
DataTable IDataProvider.GetData() => this.GetData();
}
...this way you don't need any hand-written partial
types except to add the IDataProvider<out TDataTable>
bit:
// Auto-generated by the designer:
public partial class ATableAdapter : global::system.Component
{
public virtual ADataTable GetData()
{
ADataTable aDataTable = new ADataTable();
// Do things
return aDataTable;
}
}
// Hand-written:
public partial class ATableAdapter : IDataProvider<ADataTable>
{
}