Search code examples
vb.netdatasetdatarelation

DataSet error calling GetChanges from an Expression Column 'cannot find primary relation'


Why is this code generating an error?

Dim ds As New DataSet
Dim tMaster As New DataTable(“TableMaster”)
Dim idMPmaster As DataColumn = tMaster.Columns.Add(“IdMP”, GetType(String))
tMaster.Columns.Add(“Order”, GetType(Integer))
tMaster.PrimaryKey = New DataColumn() {idMPmaster}
ds.Tables.Add(tMaster)

Dim tDetail As New DataTable(“TableDetail”)
Dim cidDetail As DataColumn = tDetail.Columns.Add(“id”, GetType(Integer))
Dim cidMPDetail As DataColumn = tDetail.Columns.Add(“idMP”, GetType(String))
Dim cOrder As DataColumn = tDetail.Columns.Add("Order", GetType(Integer))
tDetail.PrimaryKey = New DataColumn() {cidDetail, cidMPDetail}
ds.Tables.Add(tDetail)
ds.Relations.Add("Rel", idMPmaster, cidMPDetail)

tMaster.Rows.Add(“A”, 10)
tMaster.Rows.Add(“B”, 20)
tMaster.Rows.Add(“C”, 30)

tDetail.Rows.Add(1, “A”)
tDetail.Rows.Add(1, “B”)
tDetail.Rows.Add(2, “B”)
tDetail.Rows.Add(2, “C”)
tDetail.Rows.Add(3, “A”)
tDetail.Rows.Add(3, “B”)
tDetail.Rows.Add(3, “C”)

' Without this line there will be no error
cOrder.Expression = "Parent(Rel).Order"

' This line generate an error: cannot find parent relation Rel
Dim changes tDetail.GetChanges()

Why does the last line of the code generate an error:

cannot find parent relation Rel


Solution

  • You're trying to get the recent changes in a DataTable, where a DataColumn is bound to another, in a different DataTable, by means of an Expression which uses a DataRelation added to the DataSet:

    Dim relation = New DataRelation("[Relation Name]", [Parent Column], [Child Column])
    [DataSet].Relations.Add(relation)
    

    Now, the Rows in the child DataTable (the tDetail object here), rely on the DataRelation to generate values.

    At this point, the parent and child DataTables are bounded (their relation is constrained)

    To get the Rows that currently have changes (any kind, before AcceptChanges() is called) in the bounded DataTables, you need to use the DataSet, not the single DataTables. The DataSet knows what Relation binds the DataTables it references.
    If you get the changes of a single DataTable object, which has a DataColumn bound to another with an Expression that uses a DataRelation defined in a DataSet, the DataRelation cannot be found, simply because it's not there.

    Hence, you can change your code and use the DataSet to get the changes:

    ' Change from:    
    tDetail.GetChanges()
    
    ' To:  
    ds.GetChanges()
    

    The DataSet.GetChanges() method returns a new DataSet that contains all Rows that have changes, each on its own DataTable.
    If you're interested in the changes in Rows of a specific DataTable, read or count the Rows of that specific DataTable in the returned DataSet.


    Let's build the whole thing in order:

    Dim ds As New DataSet("RelDataSet")
    
    Dim tMaster As New DataTable("TableMaster")
    Dim idMPmaster = New DataColumn("IdMP", GetType(String)) With {
        .AutoIncrement = False, .Unique = True, .AllowDBNull = False
    }
    Dim orderMaster = New DataColumn("Order", GetType(Integer))
    tMaster.Columns.AddRange({idMPmaster, orderMaster})
    tMaster.PrimaryKey = New DataColumn() {idMPmaster}
    
    Dim tDetail As New DataTable("TableDetail")
    Dim detailID As New DataColumn("id", GetType(Integer))
    Dim detailIdMP As New DataColumn("idMP", GetType(String))
    Dim detailOrder As New DataColumn("Order", GetType(Integer))
    tDetail.Columns.AddRange({detailID, detailIdMP, detailOrder})
    
    ds.Tables.AddRange({tDetail, tMaster})
    ds.Relations.Add(New DataRelation("Rel", idMPmaster, detailIdMP))
    
    ' Ether Parent(Rel).Order or, since there's a single DataRelation set: 
    detailOrder.Expression = "Parent.Order"
    
    ' [... add data ...] 
    
    Dim dsChanges = ds.GetChanges()
    Dim dtMasterChanges = dsChanges.Tables(tMaster.TableName)
    Dim dtDetailsChanges = dsChanges.Tables(tDetail.TableName)
    
    ' [...]