views:

336

answers:

2

I created two tables:

Publisher

  • PK, PublisherId, int, not null, indentity +1
  • PublisherName, varchar(50), not null

Product

  • PK, ProductId, int, not null, identity +1
  • ProductName, varchar(50), not null
  • PublisherId, int, not null

Then I created a foreign key constraint FK__Product__Publisher between those tables connecting the PublisherId's.

This way I want to support Products that have one Publisher each while each Publisher can have multiple Products. It's as simple as that.

Now I created a console project in C#, added a typed dataset and two TableAdapter. One pointing to Publisher one to Product. The DataSet-Designer automatically adds the relation based on what it gets from SQL Server.

It also automatically generates properties which should allow me to access the publisher from a selected Product and all products from a selected publisher.

This is my test code:

ProductTableAdapter adapter = new ProductTableAdapter();

foreach (ProductRow product in adapter.GetData())
{
    Console.WriteLine(product.PublisherRow.PublisherName);
}

However, this does not work. With the generated code, the property PublisherRow looks like this:

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public LanguageRow PublisherRow {
    get {
        return ((PublisherRow)(this.GetParentRow(this.Table.ParentRelations["FK_Product_Publisher"])));
    }
    set {
        this.SetParentRow(value, this.Table.ParentRelations["FK_Product_Publisher"]);
    }
}

this.Table.ParentRelations does not contain the relation. It is null and accessing it results in a NullReferenceException.

What am I doing wrong here?

+1  A: 

It seems a little weird answering my own post. It's a little like talking to myself. However, I sort of found a solution and since this problem seems to be pretty common, I post it here.

Simply using the TableAdapters to get the data will not retrieve the proper relations from the database. The initialization of the relationships takes place within the dataset class (namely the InitClass method which is called from the datasets' c'tor):

public DbDataSet() {
    this.BeginInit();
    this.InitClass();
    ...
    this.EndInit();
}

The corresponding generated InitClass method looks like this:

private void InitClass() {
    ...
    this.relationFK_Product_Publisher= new global::System.Data.DataRelation("FK_Product_Publisher", new global::System.Data.DataColumn[] { this.tableProduct.PublisherIdColumn}, new global::System.Data.DataColumn[] { this.tablePublisher.PublisherIdColumn}, false);
    this.Relations.Add(this.relationFK_Product_Publisher);
}

This is why you need to instantiate the dataset class itself and use the fill method to populate your table classes like this:

DbDataSet ds = new DbDataSet();    

ProductTableAdapter productAdapter = new ProductTableAdapter();            
productAdapter.Fill(ds.Product);

PublisherTableAdapter publisherAdapter = new PublisherTableAdapter();
publisherAdapter.Fill(ds.Publisher);

foreach (ProductRow product in ds.Product)
{                
    Console.WriteLine(product.PublisherRow.PublisherName);
}
Mephisztoe
Does anyone know a better way?
Mephisztoe
A: 

Thank you for sharing the answer, it helped me.