none
Konzeptfrage: Wie finde ich meinen Primary Key in ADO.NET? RRS feed

  • Frage

  • Hallo Leute,

    ich habe eine Konzeptfrage zu ADO.NET und komme mit Google einfach nicht weiter...

    Folgendes Beispiel-Problem:
    Ich möchte in eine Tabelle Personen einen Datensatz einfügen. Dabei muss mir der PK (Personalnummer) nach dem Einfügen bekannt sein, da ich weitere Daten einfügen möchte, die sich auf die neue Person beziehen. Z.B. könnte es eine Tabelle MobilePhone geben, in der ich ein Handy einer Person zuteilen möchte. Bsp.:




    Verbinde ich mich nun via ADO.NET (DataAdapter, SQLConnection etc.) auf die Tabelle und füge einen Datensatz ein (z.B. Peter Pan), bleibt die Frage, WER die Personalnummer festlegt. Ich kenne 3 Varianten:

    1. Ich schreibe direkt eine Nummer in den Datensatz, z.b. 100.
    Dann müsste ich auf Clientseite wissen, welche Nummern noch nicht vergeben sind. Alternativ könnte ich eine Tabelle mit freien Nummern in der Datenbank speichern. Das ist aber sehr umständlich.

    2. Ich verwende als Personalnummer eine GUID, die kann ich problemlos auf dem Client erstellen.
    Das würde gehen. Jede neue GUID ist immer frei, ich kann sie selbst wählen und später im Code auf den Datensatz refernzieren. Leider sind GUIDs relativ groß und keine für Benutzer sprechenden Schlüssel. Intern kann man so etwas schon machen, vor allem dann, wenn man Datenbestände gerne Mergen möchte.

    3. Ich setze Personalnummer auf dem SQL Server als Identitätswert. So wird die Nummer automatisch erstellt.
    Die Variante gefällt mir am besten. Hier gibt es jedoch folgendes Problem: Nach dem Update wird mir die Personalnummer nicht zurück in meine Datatable geschrieben.


    Der Code für Variante 3 könnte so aussehen:
        class Personen2
        {
            public SqlConnection Connection {get;set;}
            private DataTable _dt;
            private SqlDataAdapter _SqlDataAdapter;
    
            public void CreateAGuy()
            {
                //Init
                String SQLquery = String.Format("SELECT * FROM Personen");
                _dt = new DataTable();
    
                //Daten von Server holen
                Connection.Open();
                _SqlDataAdapter = new SqlDataAdapter(SQLquery, Connection);
                _SqlDataAdapter.Fill(_dt);
                Connection.Close();
    
                //DatenAdapter für Updates vorbereiten
                SqlCommandBuilder builder = new SqlCommandBuilder(_SqlDataAdapter);
                builder.GetUpdateCommand();
    
                //Hinzufügen einer neuen Person
                var row = _dt.NewRow();
                row["Name"] = "Peter";
                row["Vorname"] = "Pan";
                row["Kommentar"] = "Currently in Neverland";
                _dt.Rows.Add(row);
    
                //Update schreiben
                Connection.Open();
                DataTable changes = _dt.GetChanges();
                _SqlDataAdapter.Update(changes);
                Connection.Close();
    
                //Welche Personalnummer (PK) hat Peter Pan??
                Console.WriteLine(row["PersonalNummer"]); //Geht leider nicht! :-(
    
            }
        }
    



    Wie komme ich nach dem Einfügen eines Datensatzes an den von der Datenbank selbst festgelegten Schlüssel? Wie füge ich einen Datensatz hinzu, der auf Peter Pan refernziert????

    Vielen Dank und viele Grüße
    Niels W.
    Viele Grüße
    Niels Wiederanders
    http://fingers4soul.de
    Dienstag, 31. Januar 2012 15:05

Antworten

  • Noch ein kleiner Nachtrag:

    Wenn Du mal davon absiehst dass Deine Anwendung für alle DB Systeme laufen soll kannst Du den RowUpdated Event benutzen um für jede in die DB eingefügte DataRow mittels "SELECT @@IDENTITY" für MSSQL bzw. "SELECT LAST_INSERT_ID()" für MySQL den Primary Key zurückerhalten.


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    • Als Antwort markiert Niels_W Mittwoch, 1. Februar 2012 08:00
    Dienstag, 31. Januar 2012 15:50

Alle Antworten

  • Hallo Niels,

    wenn ich es noch richtig im Kopf habe kannst Du mittels "SELECT @@IDENTITY" den letzten eindeutigen Schlüssel der bestehenden Verbindung zurück ( bei MSSQL ).

    Ich benutze oft Deine 2. Möglichkeit mit der GUID. Zum Anzeigen und suchen wird immer noch ein Autowert in der DB ( meist MySQL bei mir ) generiert.


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Dienstag, 31. Januar 2012 15:12
  • Hallo Hannes,

    danke für die schnelle Antwort. Ich habe es nachgelesen. Mit IDENTITY könnte man das Problem lösen, wobei ich dann sicher stellen muss, dass immer nur EIN neuer Datensatz via Update geschrieben wird. Das Löst einige Probleme, fühlt sich aber irgendwie dennoch wie ein Workaround an. Und es ist auch noch Datenbankspezifisch.

    Es bleibt also die Frage offen: Mit Update() synchronisiere ich Daten von der DataTable zur Datenbank. Gibt es keine Unviverselle Möglichkeit, im Anschluss die am Server geänderten Daten zurück zu synchronisieren?


    Viele Grüße
    Niels W.
    Dienstag, 31. Januar 2012 15:23
  • Niels,

    wenn Du das ganze unabhängig vom Datenbanksystem machen willst, dann ist meines Erachtens der Weg mittels GUID der Beste.

    Du hast immer eindeutige Werte und diese sind Dir auch schon vorher bekannt.

    Vieleicht hat aber einer der SQL Experten im Forum Dir noch eine andere Lösung.


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    Dienstag, 31. Januar 2012 15:38
  • Noch ein kleiner Nachtrag:

    Wenn Du mal davon absiehst dass Deine Anwendung für alle DB Systeme laufen soll kannst Du den RowUpdated Event benutzen um für jede in die DB eingefügte DataRow mittels "SELECT @@IDENTITY" für MSSQL bzw. "SELECT LAST_INSERT_ID()" für MySQL den Primary Key zurückerhalten.


    Hannes

    If you have got questions about this, just ask.

    In a perfect world,
    users would never enter data in the wrong form,
    files they choose to open would always exist
    and code would never have bugs.

    C# to VB.NET: http://www.developerfusion.com/tools/convert/csharp-to-vb/
    • Als Antwort markiert Niels_W Mittwoch, 1. Februar 2012 08:00
    Dienstag, 31. Januar 2012 15:50
  • Hallo Hannes,

     

    das ist genial! Habe es gerade ausprobiert. Wenn ich das ganze vernünftig als Basisklasse Kapsle, kann es auch bedingt Datenbankunabhängig werden. Zumindest für alle Datenbanken, die einen solchen Befehl erlauben.

    @All: Hier ist der Code, den Hannes beschrieben hat. Das ganze ist nur ein Auszug, und alleine nicht voll funktionsfähig.

           /// <summary>
            /// Speichert die Änderungen, die seit der letzten Abfrage verändert wurden
            /// </summary>
            public void SaveChanges()
            {
                //Abbruchbedinungen
                if (_dt == null) throw new Exception("Änderungen können nur gespeichert werden, wenn vorher Daten abgerufen wurden!");
    
                //Handler, welcher beim Update jeder einzelnen Zeile aufgerufen wird
                _SqlDataAdapter.RowUpdated += new SqlRowUpdatedEventHandler(OnRowUpdated);
    
                //Änderungen schreiben
                _SqlDataAdapter.Update(_dt);
    
                //Handler wieder entfernen
                _SqlDataAdapter.RowUpdated -= new SqlRowUpdatedEventHandler(OnRowUpdated);
            }
    
    
            /// <summary>
            /// Fügt bei jeder neuen Row in der lokalen _dt den von der Datenbank generierten Identitätswert (PK) ein
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void OnRowUpdated(object sender, SqlRowUpdatedEventArgs e)
            {
                //Prüfen, ob es sich um eine neue Zeile handelt
                var r = e.Row;
                if (r.RowState == DataRowState.Added)
                {
                    //Identität des letzten Update ermitteln
                    var IdentitySelect = "SELECT @@IDENTITY";
                    var IdentityAdapter = new SqlDataAdapter(IdentitySelect, Connection);
                    var dtIdent = new DataTable();
                    IdentityAdapter.Fill(dtIdent);
                    String IdString = dtIdent.Rows[0].ItemArray[0].ToString();
                    int Id = Convert.ToInt32(IdString);
    
                    //Identität in die row schreiben 
                    r[PK_Name] = Id;
    
                    
                }
            }
        }
    



    Viele Grüße
    Niels W.
    Mittwoch, 1. Februar 2012 08:12