2

I have a collection of objects with no key or order or other obvious index.

I wish to store data regarding each object in a DataTable. I thought an elegant way of doing this would be to store a reference in the owner column, and make that columns type typeof(MyClass).

However, when I try to do this in practice, it doesn't work (it says the primary keys collide). Turns out that putting the instances into a row field just writes "MyProgram.MyClass" into the field - presumably the output of toString even though that row's type was supposed to be MyClass not string.

Here is some sample code which works in LINQPad:

void Main()
{
    // Create a table
    var table = new DataTable();

    var ownerColumn = new DataColumn("Owner", typeof(MyClass));

    var primaryKey = new[] { ownerColumn };

    table.Columns.AddRange(primaryKey);
    table.PrimaryKey = primaryKey;

    table.Columns.Add(new DataColumn("Some Data", typeof(int)) { DefaultValue = 0 });

    // Create 2 objects
    var c1 = new MyClass();
    var c2 = new MyClass();

    // Store their data in the table
    var row = table.NewRow();
    row["Owner"] = c1;
    row["Some Data"] = 1;
    table.Rows.Add(row);

    row = table.NewRow();
    row["Owner"] = c2;
    row["Some Data"] = 2;
    table.Rows.Add(row);
}

// Define other methods and classes here
class MyClass {

}

What do I do to solve this? Do I have to make an id field in MyClass, then use id to fill in the owner column, and then make sure each object receives a unique id at creation myself?

Superbest
  • 25,318
  • 14
  • 62
  • 134
  • Are you sure that the code does not work? Looking at the .NET code there is no reason why this would not work. What is the output of `row["Owner"].GetType()`? – Knaģis Nov 05 '12 at 12:59
  • @Knaģis It's `typeof (MyClass)`. Also, it throws an exception in LinqPad and unless I'm mistaken compiled C# code as well. – Superbest Nov 05 '12 at 13:42
  • ok, so the requirement was to use MyClass as the primary key for the DataTable? – Knaģis Nov 05 '12 at 13:58
  • @Knaģis I think so. I was trying to use references to instances of `MyClass` as the key, so that there can not be two rows pointing to the same object. – Superbest Nov 05 '12 at 14:03
  • Added a new answer describing what is needed to use `MyClass` as the primary key. – Knaģis Nov 05 '12 at 14:04

2 Answers2

2

You have to implement System.IComparable (non-generic version) interface on MyClass so that DataTable knows how to to compare the value of the column. If this interface is not defined, the code falls back on comparing object.ToString() results.

Knaģis
  • 20,827
  • 7
  • 66
  • 80
  • Thanks, that works! But with the thing I am trying to program, it is illogical for MyClass instances to have a "sort order". I implemented the method as `public int CompareTo(object other) { return other == this ? 0 : 1; }` but is there a less hacky way? It seems implementing IEquatable doesn't work. – Superbest Nov 05 '12 at 14:21
  • 1
    I would advise of implementing the IComparable so that it returns a deterministic sort order, for example, using `return other == this ? 0 : this.GetHashCode().CompareTo(other.GetHashCode())` (just remember to check for null-s. The reason is that some collections actually check if the CompareTo results are meaningful (for example, checks `if a > b then b must be < a`. – Knaģis Nov 05 '12 at 14:36
  • Thanks, I hadn't thought of nulls or hash codes. The problem in my question is resolved at this point, however, I think with your version two objects can be evaluated equal if their hashes collide (different objects -> checks hash -> hashes happen to be the same -> hash comparison returns 0 anyway). My intention is that `MyClass.CompareTo` returns 0 if *and only if* the two references point to the same place, and not to merely objects with equal hashes. – Superbest Nov 05 '12 at 15:31
  • Nevermind, I was able to find a satisfactory hash function (and in fact I think the default implementation is also satisfactory): http://stackoverflow.com/questions/1139767/object-gethashcode – Superbest Nov 05 '12 at 16:00
1

You can use auto increment column :

DataTable dTable = new DataTable();
DataColumn auto = new DataColumn("AutoID", typeof(System.Int32));
dTable.Columns.Add(auto);
auto.AutoIncrement = true;
auto.AutoIncrementSeed = 1;
auto.ReadOnly = true;
Damith
  • 62,401
  • 13
  • 102
  • 153
  • I'm not sure but won't this lead to problems with normalization? Specifically, it will become possible for me to have two rows with different "AutoID" values but the same "Owner", unless I make both of them primary keys in which key my primary key will become redundant. (I made a separate question here: http://stackoverflow.com/questions/13233088/normalize-a-table-with-a-two-column-primary-key-and-one-other-column) – Superbest Nov 05 '12 at 13:45