Announcement

Collapse
No announcement yet.

DataTable und PrimaryKey

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • DataTable und PrimaryKey

    Hallo,

    ich greife über ADO.NET auf eine Access Datenbank zu. Wenn ich das DataSet über die Methode Fill() des DataAdapters fülle, übernimmt er meinen Primary Key der Access-Tabelle nicht mit. Jedesmal den PrimaryKey im DataAdapter von Hand anzulegen ist doch etwas unkomfortabel und ich denke mal, dass ADO.NET eigentlich so fortschrittlich sein müsste und die PrimaryKey-Werte aus der Datenbank auslesen können sollte. Was mache ich falsch???

    ciao Christian Stasius

  • #2
    Hallo,

    immer dann, wenn sich der Entwickler das Leben erleichtern will, kann er durch den Aufruf der Methode <b>FillSchema</b> ADO.NET beauftragen, alle benötigten Metadaten aus dem Datenbank-Schema (alias Systemtabellen) auszulesen. ADO.NET greift dabei auf die Tabelle zu, die von der DataSet-Eigenschaft <b>SelectCommand</b> festgelegt wird. Die Methode FillSchema verwendet die aus der Datenbank ausgelesenen Informationen dazu, um ein DataTable-Objekt der Tables-Kollektion des DataSets hinzuzufügen. Danach wird die DataColumnCollection-Kollektion dieser DataTable-Objektinstanz gefüllt, wobei jedes einzelne DataColumn-Objekt entsprechend den Metadaten in den folgenden Eigenschaften konfiguriert wird:<br>
    - AllowDBNull<br>
    - AutoIncrement (allerdings müssen AutoIncrementStep und AutoIncrementSeed separat konfiguriert werden) <br>
    - MaxLength <br>
    - ReadOnly <br>
    - Unique <br>
    Die Methode FillSchema konfiguriert ebenfalls die <b>PrimaryKey</b>- und <b>Constraints</b>-Eigenschaft.

    Im Fall einer MS SQL Server 2000-Datenbank sieht das so aus:
    <pre>
    ' DataSet mit dem Inhalt der beiden Tabellen füllen
    Dim aCmd As New SqlCommand("SELECT * FROM Customers", aCon)
    Dim aAdp As New SqlDataAdapter(aCmd)
    Dim aDS As New DataSet()
    aCon.Open()
    aAdp.FillSchema(aDS, SchemaType.Mapped, "Customers")
    aAdp.Fill(aDS, "Customers")
    aCmd.CommandText = "SELECT * FROM Orders"
    aAdp.FillSchema(aDS, SchemaType.Mapped, "Orders")
    aAdp.Fill(aDS, "Orders")
    aCon.Close()
    </pre&gt

    Comment


    • #3
      Hallo Andreas,

      ich habe mit großem Interesse das gelesen, was Du vor 5 Jahren geschrieben hast und hätte dazu eine, oder zwei ("darf es ein bißchen mehr sein?") Fragen:

      Das Problem ist immer noch dasselbe, die Person hat gewechselt, ein DatagridView ist dazugekommen und hat eine Meldung für mich bereit:

      "Der Datensatz kann nicht geändert oder gelöscht werden, da die Tabelle XY in Beziehung stehende Datensätze enthält!"

      Mein Code:
      'Code-Anfang
      JagdgebietGemeindeDa = New OleDbDataAdapter(oTblDef.pJagdgebietJoinGemeinde, conOle)
      'Kommentar-Anfang
      OTblDef.oTblDef.pJagdgebietJoinGemeinde enthält SelectCommandString
      'Kommentar-Ende
      JagdgebietGemeindeDt = ds.Tables.Add("tbl2100JagdgebietGemeinde")
      'Kommentar: tbl2100JagdgebietGemeinde ist eine Tabelle in einer Access-DB
      JagdgebietGemeindeDa.FillSchema(ds, SchemaType.Mapped, JagdgebietGemeindeDt.TableName.ToString)
      JagdgebietGemeindeDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
      JagdgebietGemeindeDa.FillSchema(JagdgebietGemeinde Dt, SchemaType.Mapped)
      JagdgebietGemeindeDa.Fill(JagdgebietGemeindeDt)
      'Code-Ende

      hat leider keinen Erfolg!

      Führt Datatable.PrimaryKey definieren zum Erfolg ? - Wenn ja - Wie?

      Liebe Grüße aus dem Dot.Net Nirvana
      Christian

      Comment


      • #4
        Hallo,

        Der Datensatz kann nicht geändert oder gelöscht werden, da die Tabelle XY in Beziehung stehende Datensätze enthält!
        dieses Veto bezieht sich auf eine Relation (Beziehung zwischen 2 DataTable-Instanzen im DataSet). Da ein DataGridView verwendet wird (.NET 2.0), sollte für die Tabellen ein typisiertes DataSet eingerichtet werden. In diesem Fall kümmert sich Visual Studio um die Konfiguration der Beziehungen (siehe Abbildung)
        Attached Files

        Comment


        • #5
          Originally posted by Andreas Kosch View Post
          ...kümmert sich Visual Studio um die Konfiguration...
          Lieber Andreas (danke für deine Antwort!), Liebes Forum,

          ich hätte aber, ohne den Rat, den VS-Designer für mich arbeiten zu lassem, in den Wind schlagen zu wollen, trotzdem gerne gewußt, wie denn das Übernehmen eines "PrimaryKeys" aus einer Datenbank-Tabelle in ein Dataset-Datatable funktioniert?

          Ich hänge leider in einer (geistigen) Schleife:

          'Code-Anfang
          Dim ds as Dataset
          ds.tables.add("tbl1100Gemeinde")
          GemeindeDa.FillSchema(ds, SchemaType.Mapped, GemeindeDt.TableName.ToString)
          GemeindeDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
          GemeindeDa.FillSchema(GemeindeDt, SchemaType.Mapped)
          Me.ds.Tables.Item("tbl1100Gemeinde").PrimaryKey.Se tValue(GemeindeDt.Columns.Item(0), Index as Integer)
          'Code-Ende

          Für Index as Integer = 0 oder 1 oder sonstwas bekomme ich bloß
          "Der Index war außerhalb des Array-Bereichs"! als Exception-Antwort.

          Wer klärt mich auf?

          Gruß
          Christian

          Comment


          • #6
            Originally posted by cb4866 View Post
            ich hätte aber, ohne den Rat, den VS-Designer für mich arbeiten zu lassem, in den Wind schlagen zu wollen, trotzdem gerne gewußt, wie denn das Übernehmen eines "PrimaryKeys" aus einer Datenbank-Tabelle in ein Dataset-Datatable funktioniert?
            Hallo Christian,

            für solche Fälle empfiehlt sich immer dieses Vorgehen: Lass eine Situation vom Designer bearbeiten und untersuche die zusätzlich erzeugte Datei MyTableAdapter.Designer.vb (oder wie auch immer diese heißt). Was dort gemacht wird, kannst Du für Deine eigenen Zwecke auch selbst manuell ausführen. (Auf diesem Weg erzeuge ich z.B. inzwischen xsd-Dateien manuell und bekomme dafür schnell ein typ. DataSet. Auch wie Formulare zur Laufzeit zusammengebastelt werden können, geht damit.)

            Jürgen

            Comment


            • #7
              Hallo,

              ..wie denn das Übernehmen eines "PrimaryKeys" aus einer Datenbank-Tabelle in ein Dataset-Datatable funktioniert?
              Das folgende Beispiel demonstriert, wie die Relations-Sammlung des DataSets mit einer neu konfigurierten DataRelation-Instanz befüllt wird.

              Code:
              ' DataSet mit dem Inhalt der beiden Tabellen füllen
                Dim aCmd As New SqlCommand("SELECT * FROM Customers", aCon)
                Dim aAdp As New SqlDataAdapter(aCmd)
                Dim aDS As New DataSet()
                aCon.Open()
                aAdp.FillSchema(aDS, SchemaType.Mapped, "Customers")
                aAdp.Fill(aDS, "Customers")
                aCmd.CommandText = "SELECT * FROM Orders"
                aAdp.FillSchema(aDS, SchemaType.Mapped, "Orders")
                aAdp.Fill(aDS, "Orders")
                aCon.Close()
              ' Beziehungen zwischen diesen beiden Tabellen klären
                Dim aRel As New DataRelation("CustomersOrders", _
                          aDS.Tables("Customers").Columns("CustomerID"), _
                          aDS.Tables("Orders").Columns("CustomerID"), _
                          CheckBoxCONSTRAINTS.Checked)
                aDS.Relations.Add(aRel)
              Nach dem Aufruf der Add-Methode "kennt" das DataSet die Fremdschlüsselspalte CustomerID, so dass die Beziehungen zwischen den beiden Tabellen auch im DataSet wirksam sind. Zum besseren Verständnis kann man sich auch die Beziehungen anzeigen lassen:
              Code:
                      ' Beziehungen anzeigen 
                      Dim aDT As DataTable
                      Dim aNodeParent, aNodeContraint As TreeNode
                      TreeView1.Nodes.Clear()
                      For Each aDT In aDS.Tables
                          aNodeParent = TreeView1.Nodes.Add(aDT.TableName)
                          aNodeParent.Nodes.Add("Constraints.Count " _
                             + aDT.Constraints.Count.ToString())
                          aNodeParent.Nodes.Add("ParentRelations.Count " _
                             + aDT.ParentRelations.Count.ToString())
                          aNodeParent.Nodes.Add("ChildRelations.Count " _
                             + aDT.ChildRelations.Count.ToString())
                          aNodeContraint = aNodeParent.Nodes.Add("Constraints")
                          Dim aCstrn As Constraint
                          For Each aCstrn In aDT.Constraints
                              aNodeContraint.Nodes.Add(aCstrn.ConstraintName)
                              aNodeContraint.Nodes.Add(aCstrn.GetType().ToString)
                          Next aCstrn
                      Next aDT

              Comment


              • #8
                Hallo Jürgen, Hallo Andreas,
                zwei auf Einen - so muss es er es kapieren - habt herzlichen Dank für Eure Geduld!
                Gruß
                Christian

                Comment


                • #9
                  Hallo liebes Forum,

                  die Information, die ich von Andreas und Jürgen bekommen habe, führten leider nicht zur Lösung des Problems, sondern nur zur Eingrenzung:
                  Die Exception-Meldung des DatagridViews: "Der Datensatz kann nicht geändert oder gelöscht werden, da die Tabelle XY in Beziehung stehende Datensätze enthält!" tritt auch in der Designer-Version auf! Daraus schließe ich, dass das Problem leider nicht an meinem Code, sondern vermutlich daran, dass der DataAdapter mit m:n Relationen nicht umgehen kann ?????

                  Vielleicht habe ich im Datenbankdesign einen Fehler? Deshalb meine Frage: Ist meine Einschätzung dass für die Situation:
                  Tabelle "tblSchüler" mit den Feldern "indexSchüler" und "NameSchüler"
                  Tabelle"tblLehrer" mit den Feldern "indexLehrer" und "NameLehrer",

                  um nun feststellen zu können,
                  von wievielen Lehrern ein Schüler unterrichtet wird und
                  welche Schüler ein Lehrer unterrichtet,
                  ist eine weitere
                  Tabelle "tblLehrerJoinSchüler" mit den Feldern "indexLehrerJoinSchüler", "Fremdschlüssel_Lehrer" und "Fremdschlüssel_Schüler" erforderlich?

                  Wenn dieses Szenario im Datenbank-Design erlaubt ist, wie muss die .Net Lösung aussehen? Die Definition der zwei 1:n Beziehungen nämlich:
                  1. tblLehrer zu tblLehrerJoinSchüler und
                  2. tblSchüler zu tblLehrerJoinSchüler
                  hat nichts gebracht!

                  liebe Grüße
                  Christian

                  Comment


                  • #10
                    Originally posted by cb4866 View Post
                    Daraus schließe ich, dass das Problem leider nicht an meinem Code, sondern vermutlich daran, dass der DataAdapter mit m:n Relationen nicht umgehen kann ?????
                    So ist es (das liegt übrigens nicht am DataAdapter, sondern am DataSet). Eine DataRelation "ist vergleichbar mit einer Primärschlüssel/Fremdschlüssel-Beziehung" (SDK-Doku); aber genau diese Situation ist eben nicht gegeben.
                    Vielleicht habe ich im Datenbankdesign einen Fehler? ... um nun feststellen zu können, von wievielen Lehrern ein Schüler unterrichtet wird und welche Schüler ein Lehrer unterrichtet, ist eine weitere Tabelle "tblLehrerJoinSchüler" mit den Feldern "indexLehrerJoinSchüler", "Fremdschlüssel_Lehrer" und "Fremdschlüssel_Schüler" erforderlich?

                    Wenn dieses Szenario im Datenbank-Design erlaubt ist, wie muss die .Net Lösung aussehen?
                    Das ist dann kein Fehler im Datenbank-Design, sondern ein sinnvolles Verfahren:
                    Originally posted by Andreas Kosch: Delphi 2.0 Datenbankentwicklung (1996, S. 92)
                    In einer "n:m"-Tabellenbeziehung zwischen zwei Tabellen muss die Beziehung selbst in einer weiteren Tabelle gespeichert werden. Jeder Datensatz in "Treffer" speichert nur numerische Werte für die Datensatznummer des Artikeldatensatzes und die Begriffsnummer des zugeordneten Begriffs. Beide Felder bilden zudem den zusammengesetzten Primärschlüssel.
                    Das ist einer der wenigen Fälle, wo ein zusammengesetzter Primärschlüssel sinnvoll ist; auf ein Feld indexLehrerJoinSchüler kannst Du also verzichten.

                    Wie die NET-Lösung aussieht, hast Du damit auch schon skizziert: zusätzliche Join-Tabelle, DataRelations auf Lehrernummer und Schülernummer setzen.

                    Gruß Jürgen

                    Comment


                    • #11
                      Liebes Forum
                      Hallo Jürgen,

                      ...zusätzliche Join-Tabelle, DataRelations auf Lehrernummer und Schülernummer setzen
                      im angehängten Zip-Ordner befindet sich eine pdf-Datei mit einem Screen-Shot - (Ich habe das TreeView Steuerelement nur zwecks Darstellung über das DatagridView gelegt!)

                      Vielleicht kann irgendwer etwas damit anfangen - vielleicht habe ich etwas übersehen - es funktioniert einfach nicht :-|

                      Falls der Code für das Dataset oder das DatagridView zur Auflärung erforderlich ist, stelle ich ihn sofort in dem vom Sysop tolerierten Dosen zur Verfügung ;-)
                      Attached Files

                      Comment


                      • #12
                        Tut mir leid - Fehler in gepackter Datei...

                        Ich verstehe inzwischen nicht mehr, was Du eigentlich erreichen willst und was dabei nicht klappt (ursprünglich ging es um PrimaryKey, dann um DataRelation mit 1:n, dann um m:n-Beziehung und jetzt um DataGridView). Vielleicht hilfst Du mir beim Verständnis?

                        Nebenbei: das DataGridView ist zuständig für die Daten genau einer Tabelle. Lediglich über DataGridViewComboBoxColumn können Nachschlagetabellen (d.h. über DataRelation/ForeignKey, also 1:n-Beziehung) einbezogen werden.

                        Gruß Jürgen

                        Comment


                        • #13
                          Hallo Jürgen,

                          ich habe die pdf-Datei im Zip-Ordner durch eine doc ersetzt hoffentlich klappt es jetzt!

                          kurze Beschreibung:
                          Die Daten sind in einer Access 2003 mdb.
                          Formular mit 1 Datagridview
                          Datagridview mit 1 Combobox

                          Code für Dataset - Anfang:

                          Private Function InitDataSet() As Boolean
                          Debug.Print("")
                          Debug.Print("InitDataSet - Anfang")
                          Dim oTblDef As New clsTableDef
                          Try
                          InitDataSet = False
                          Do Until InitDataSet = True
                          ' DataSet erzeugen
                          ds = New DataSet(Me.thisForm.thisFromName.ToString + "Dataset")
                          Debug.Print("ds.DataSetName = " + ds.DataSetName.ToString)
                          'Master Tabelle in DataSet einfügen
                          MasterDa = New OleDbDataAdapter(oTblDef.pJagdgebiet, conOle)
                          'Achtung der Index: listItem(0) bedeutet, dass im ersten Block
                          'von(myGridColumnCollection) der Name der MasterTabelle stehen muss!
                          MasterDt = ds.Tables.Add( _
                          Me.myGridColumnCollection.listItem(0).colSourceTab leName.ToString)
                          MasterDa.FillSchema(ds, SchemaType.Mapped, MasterDt.TableName.ToString)
                          MasterDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
                          Debug.Print(MasterDa.SelectCommand.CommandText.ToS tring)
                          MasterDa.FillSchema(MasterDt, SchemaType.Mapped)
                          MasterDa.Fill(MasterDt)
                          'Filiale-Tabelle in DataSet einfügen
                          Me.fnInitMasterDa()

                          'JagdgebietArt
                          JagdgebietArtDa = New OleDbDataAdapter(oTblDef.pJagdgebietArt, _
                          conOle)
                          JagdgebietArtDt = ds.Tables.Add(Me.myGridColumnCollection.listItem( _
                          fnColIndex("fkJagdArt")).colCbxDatasource.ToString )
                          JagdgebietArtDa.FillSchema(ds, _
                          SchemaType.Mapped, _
                          JagdgebietArtDt.TableName.ToString)
                          JagdgebietArtDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
                          JagdgebietArtDa.Fill(JagdgebietArtDt)

                          'JagdgebietGemeinde
                          JagdgebietGemeindeDa = New OleDbDataAdapter(oTblDef.pJagdgebietJoinGemeinde, _
                          conOle)
                          JagdgebietGemeindeDt = ds.Tables.Add("tbl2100JagdgebietGemeinde")
                          JagdgebietGemeindeDa.FillSchema(ds, _
                          SchemaType.Mapped, _
                          JagdgebietGemeindeDt.TableName.ToString)
                          JagdgebietGemeindeDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
                          JagdgebietGemeindeDa.Fill(ds, "tbl2100JagdgebietGemeinde")

                          GemeindeDa = New OleDbDataAdapter(oTblDef.pGemeinde, conOle)
                          GemeindeDt = ds.Tables.Add("tbl1100Gemeinde")
                          GemeindeDa.FillSchema(ds, _
                          SchemaType.Mapped, _
                          GemeindeDt.TableName.ToString)
                          GemeindeDa.MissingSchemaAction = MissingSchemaAction.AddWithKey
                          GemeindeDa.Fill(ds, "tbl1100Gemeinde")

                          'Verknüpfungen zwischen den Tabellen herstellen
                          Try
                          relMasterToJagdgebietArt = ds.Relations.Add _
                          ("rel_Jagdgebiet_JagdgebietArt", _
                          JagdgebietArtDt.Columns.Item("GUIDJagdgebietArt"), _
                          MasterDt.Columns.Item("fkJagdArt"), _
                          True)

                          relGemeindeToJagdgebietGemeinde = ds.Relations.Add _
                          ("rel_Gemeinde_JagdgebietGemeinde", _
                          GemeindeDt.Columns.Item("guidGemeinde"), _
                          JagdgebietGemeindeDt.Columns.Item("fkGuidGemeinde" ), _
                          True)

                          relMasterToJagdgebietGemeinde = ds.Relations.Add _
                          ("rel_Jagdgebiet_JagdgebietGemeinde", _
                          MasterDt.Columns.Item("GUIDJagdgebiet"), _
                          JagdgebietGemeindeDt.Columns.Item("fkGuidJagdgebie t"), _
                          True)

                          Catch exRel As System.Data.DataException
                          Debug.Print(exRel.Message.ToString)
                          End Try

                          'neuer Code Ende
                          InitDataSet = True
                          Loop
                          Catch ex As OleDbException
                          Debug.Print(ex.Message.ToString)
                          MessageBox.Show(ex.Message.ToString)
                          Return False
                          End Try
                          Debug.Print("InitDataSet - Ende")
                          Debug.Print("")
                          End Function

                          Code für Dataset - Ende:
                          Zuletzt editiert von cb4866; 16.03.2008, 17:39.

                          Comment


                          • #14
                            Hallo liebes Forum,
                            lieber Andreas, lieber Jürgen,

                            Danke für Eure Hilfe - Ich habe den Bug (himmel.....undzwirnnochmal) gefunden!

                            Die Ursache für die Fehlermeldung des DatagridViews lag in der fehlenden Access-Verknüpfungseigenschafts-Vereinbarung "Aktualisierungsweitergabe = true"!!

                            Gruß
                            Christian

                            Comment

                            Working...
                            X