none
DataSet - Relationen definieren RRS feed

  • Frage

  • Hallo zusammen,

    ich möchte in Visual Studio Relationen zwischen Tabellen erstellen und bin dabei

    wie folgt vorgegangen:

    Public ds As New DataSet("MyDataset")
    
     DataColumnParent = ds.Tables("Kategorien").Columns("Kategorie")
     DataColumnChild = ds.Tables("Kunden").Columns
    ("Kategorie")
    
     _Relation01 = New System.Data.DataRelation("FK-Kategorie", DataColumnParent, DataColumnChild)
                    
    ds.Relations.Add(_Relation01)

    Leider klappt das nicht. Die Aktualisierungsweitergabe funktioniert, aber bei der Löschweitergabe

    werden die untergeordneten Datensätze gelöscht. Daraufhin habe ich es so probiert:

    DataColumnParent = ds.Tables("Kategorien").Columns("Kategorie")
     DataColumnChild = ds.Tables("Kunden").Columns
    ("Kategorie")
                    
    Dim _Relation_FK As ForeignKeyConstraint = New ForeignKeyConstraint("_Relation_FK", DataColumnParent, DataColumnChild)
    
    _Relation_FK.DeleteRule = Rule.None
    _Relation_FK.UpdateRule = Rule.Cascade
    _Relation_FK.AcceptRejectRule = AcceptRejectRule.Cascade
    
    ds.Tables("Kunden").Constraints.Add(_Relation_FK)
    
    ds.EnforceConstraints = True

    Leider das gleiche Ergebnis.

    Was mache ich falsch?

    Dirk

    Freitag, 18. Januar 2013 18:30

Antworten

  • Hallo Dirk,

    das DataSet "weiß" nichts von den Inhalten in der Datenbank; getestet wird immer gegen die internen Daten. Und da funktioniert es so:

        Public Sub DataSetUpdateCascadeNoDelete()
            Dim ds As New DataSet("DataSet1")
            Dim parentTable = ds.Tables.Add("ParentTable")
            Dim parentIDColumn = parentTable.Columns.Add("ParentID", GetType(Integer))
            parentTable.PrimaryKey = New DataColumn() {parentIDColumn}
    
            Dim childTable = ds.Tables.Add("ChildTable")
            Dim childIDColumn = childTable.Columns.Add("ChildID", GetType(Integer))
            Dim childParentIDColumn = childTable.Columns.Add("ChildParentID", GetType(Integer))
            childTable.PrimaryKey = New DataColumn() {childIDColumn}
    
            ' Einschränkung mit Update, jedoch ohne Delete
            Dim childParentForeignKeyConstraint = New ForeignKeyConstraint(
                "FK_Child_Parent", parentIDColumn, childParentIDColumn) With
                {
                    .UpdateRule = Rule.Cascade,
                    .DeleteRule = Rule.None,
                    .AcceptRejectRule = AcceptRejectRule.Cascade
                }
    
            childTable.Constraints.Add(childParentForeignKeyConstraint)
    
            ' Zeile für Aktualisierungstest
            parentTable.Rows.Add(1000)
            childTable.Rows.Add(1001, 1000)
            childTable.Rows.Add(1002, 1000)
    
            ' Zeile für Löschtest
            parentTable.Rows.Add(2000)
            childTable.Rows.Add(2001, 2000)
    
            ds.AcceptChanges()
            ds.EnforceConstraints = True
    
            Try
                Dim updateRows = parentTable.Select("ParentID = 1000")
                updateRows(0)(parentIDColumn) = 10000
                Console.WriteLine("ParentID 1000 updated")
            Catch ex As Exception
                Console.WriteLine("ParentID 1000 update failed " & ex.Message)
            End Try
    
            Try
                Dim deleteRows = parentTable.Select("ParentID = 2000")
                deleteRows(0).Delete()
                Console.WriteLine("ParentID 2000 deleted")
            Catch ex As Exception
                Console.WriteLine("ParentID 2000 delete failed " & ex.Message)
            End Try
    
            For Each childRow As DataRow In childTable.Rows
                Console.WriteLine("{0} => {1}", childRow(0), childRow(1))
            Next
        End Sub
    

    Zu beachten wäre: Eine Fremdschlüsseleinschränkung funktioniert nur dann, wenn Du auch die dazugehörigen Datenzeilen geladen hast. Ändert sich etwas in der Zwischenzeit wird man das erst beim Aktualisieren über den DataAdapter mitbekommen.

    Gruß Elmar

    Sonntag, 20. Januar 2013 11:46
    Beantworter
  • Hallo Dirk,

    wie in der letzten Antwort schon geschrieben:
    Du musst jeweils dafür sorgen, dass das DataSet die notwendigen Daten geladen hat. Insofern nichts anderes als bei einer Access-Datenbank auch - nur das man im DataSet nicht immer alle Daten lädt.

    Wenn Du wie oben eine Referenz auf die Tabelle selbst machst, müsste die Zeile auf die ParentID verweisen soll bereits geladen bzw. erzeugt worden sein. Ist der Eintrag nicht immer vorhanden, sollte die Spalte Nullable sein und mit DBNull vorbelegt werden.

    Für die Verwaltung von solchen Verweisen eignet sich am besten eine ComboBox mit gültigen ParentIDs. Was auf der anderen Seite erfordert dass man alle Daten lädt.

    Gruß Elmar

    Montag, 21. Januar 2013 18:00
    Beantworter

Alle Antworten

  • Hallo Dirk,

    die Frage wäre: Welches Verhalten willst Du am Ende erreichen?

    Normalerweise wird man die Kind-Datensätze ebenfalls löschen wollen, denn ansonsten ist die referentielle Integrität verletzt.

    Zu beachten wäre, dass im DataSet die Zeilen zunächst nur als gelöscht markiert werden - via DataRowState.Deleted. Gelöschte Zeilen werden in der Standard-Einstellung von einer DataView nicht angezeigt - siehe RowStateFilter.

    Sie sind aber noch vorhanden, so dass sie z. B. ein Table-/DataAdapter in der Datenbank löschen kann. Erst durch ein AcceptChanges - je nach AcceptRejectRule - werden sie endgültig entfernt.

    Gruß Elmar

    Freitag, 18. Januar 2013 20:44
    Beantworter
  • Hallo Elmar,

    Danke für die schnelle Antwort.

    Mit MS-Access lassen sich die Kind-Datensätze nur löschen, wenn ich die Löschweitergabe aktiviere.

    Wenn ich lediglich die Aktualisierungsweitergabe bei den Beziehungen aktiviere, lassen sich die

    Kind-Datensätze nur aktualisieren. Der übergeordnete Datensatz lässt sich dann nicht löschen.

    Ich möchte, dass sich der übergeordnete Datensatz nur löschen lässt, wenn sich keine

    Kind-Datensätze mehr in der DB befinden. Eine Aktualisierungsweitergabe ist natürlich

    vorgesehen.

    Wie bekomme ich das hin?

    Dirk

    Samstag, 19. Januar 2013 11:32
  • Hallo Dirk,

    das DataSet "weiß" nichts von den Inhalten in der Datenbank; getestet wird immer gegen die internen Daten. Und da funktioniert es so:

        Public Sub DataSetUpdateCascadeNoDelete()
            Dim ds As New DataSet("DataSet1")
            Dim parentTable = ds.Tables.Add("ParentTable")
            Dim parentIDColumn = parentTable.Columns.Add("ParentID", GetType(Integer))
            parentTable.PrimaryKey = New DataColumn() {parentIDColumn}
    
            Dim childTable = ds.Tables.Add("ChildTable")
            Dim childIDColumn = childTable.Columns.Add("ChildID", GetType(Integer))
            Dim childParentIDColumn = childTable.Columns.Add("ChildParentID", GetType(Integer))
            childTable.PrimaryKey = New DataColumn() {childIDColumn}
    
            ' Einschränkung mit Update, jedoch ohne Delete
            Dim childParentForeignKeyConstraint = New ForeignKeyConstraint(
                "FK_Child_Parent", parentIDColumn, childParentIDColumn) With
                {
                    .UpdateRule = Rule.Cascade,
                    .DeleteRule = Rule.None,
                    .AcceptRejectRule = AcceptRejectRule.Cascade
                }
    
            childTable.Constraints.Add(childParentForeignKeyConstraint)
    
            ' Zeile für Aktualisierungstest
            parentTable.Rows.Add(1000)
            childTable.Rows.Add(1001, 1000)
            childTable.Rows.Add(1002, 1000)
    
            ' Zeile für Löschtest
            parentTable.Rows.Add(2000)
            childTable.Rows.Add(2001, 2000)
    
            ds.AcceptChanges()
            ds.EnforceConstraints = True
    
            Try
                Dim updateRows = parentTable.Select("ParentID = 1000")
                updateRows(0)(parentIDColumn) = 10000
                Console.WriteLine("ParentID 1000 updated")
            Catch ex As Exception
                Console.WriteLine("ParentID 1000 update failed " & ex.Message)
            End Try
    
            Try
                Dim deleteRows = parentTable.Select("ParentID = 2000")
                deleteRows(0).Delete()
                Console.WriteLine("ParentID 2000 deleted")
            Catch ex As Exception
                Console.WriteLine("ParentID 2000 delete failed " & ex.Message)
            End Try
    
            For Each childRow As DataRow In childTable.Rows
                Console.WriteLine("{0} => {1}", childRow(0), childRow(1))
            Next
        End Sub
    

    Zu beachten wäre: Eine Fremdschlüsseleinschränkung funktioniert nur dann, wenn Du auch die dazugehörigen Datenzeilen geladen hast. Ändert sich etwas in der Zwischenzeit wird man das erst beim Aktualisieren über den DataAdapter mitbekommen.

    Gruß Elmar

    Sonntag, 20. Januar 2013 11:46
    Beantworter
  • Dank Dir, es funktioniert prima.....

    Dirk

    Montag, 21. Januar 2013 09:06
  • Probleme ergeben sich jetzt bei einer hierarischen 1 - n Verbindung.

    DataColumnParent = ds.Tables("Kunden").Columns("ID")
    DataColumnChild = ds.Tables("Kunden").Columns("ParentId")
    
    _Relation = New System.Data.DataRelation("Testy",   
                DataColumnParent, DataColumnChild)
    
    ds.Relations.Add(_Relation)
    

    Bekomme beim Pflegen/Updaten die Fehlermeldung:

    Für ForeignKeyConstraint Testy müssen die untergeordneten Schlüsselwerte in der übergeordneten

    Tabelle vorhanden sein.

    Kann ich das irgendwie einstellen?

    Danke Dirk

    Montag, 21. Januar 2013 16:40
  • Hallo Dirk,

    wie in der letzten Antwort schon geschrieben:
    Du musst jeweils dafür sorgen, dass das DataSet die notwendigen Daten geladen hat. Insofern nichts anderes als bei einer Access-Datenbank auch - nur das man im DataSet nicht immer alle Daten lädt.

    Wenn Du wie oben eine Referenz auf die Tabelle selbst machst, müsste die Zeile auf die ParentID verweisen soll bereits geladen bzw. erzeugt worden sein. Ist der Eintrag nicht immer vorhanden, sollte die Spalte Nullable sein und mit DBNull vorbelegt werden.

    Für die Verwaltung von solchen Verweisen eignet sich am besten eine ComboBox mit gültigen ParentIDs. Was auf der anderen Seite erfordert dass man alle Daten lädt.

    Gruß Elmar

    Montag, 21. Januar 2013 18:00
    Beantworter
  • Hallo Dirk2006,

    Ich gehe davon aus, dass die Antworten Dir weitergeholfen haben.
    Solltest Du noch "Rückfragen" dazu haben, so gib uns bitte Bescheid.

    Grüße,
    Robert


    Robert Breitenhofer, MICROSOFT   Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Freitag, 25. Januar 2013 14:02
    Moderator