DataSet / BindingSource Neuen Datensatz als Kopie von bestehendem anlegen

  • VB.NET

Es gibt 9 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    DataSet / BindingSource Neuen Datensatz als Kopie von bestehendem anlegen

    Guten Tag,

    in einem aktuellen Projekt habe ich ein Formular, welches über eine BindingSource an eine Tabelle eines DataSets geknüpft ist. In dem Formular nutze ich einen Button zum anlegen eines neuen Datensatzes:

    VB.NET-Quellcode

    1. BindingSource.AddNew()


    Nun möchte ich einen weiteren Button hinzufügen, mit welchem es möglich ist den aktuell offenen Datensatz (vielleicht über BindingSource.Current.Row) als neuen (quasi als Kopie/Duplikat) einzufügen und bearbeitbar zu machen.

    Welchen Ansatz würde ihr wählen? Die AddNew Methode lässt scheinbar keine row übergeben.

    MfG. VBDev
    var coffee = new coffee();
    if(coffee.empty)
    {
    coffee.refill();
    } else {
    coffee.drink();
    }
    Nimm direkt die DataTable her, um deren Inhalt zu manipulieren. In diesem Szenario nutz(t)e ich die BindingSource standardmäßig nur, um entweder die aktuelle DataTable-Row oder die an einer bestimmten Position zu erhalten. Aber zur Inhaltsmanipulation immer die DataTable direkt.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Danke, dann brauche ich die BindingSource ja wirklich für Current.Row

    Wie mache ich es dann mit der direkten manipulation? Die Ausgabe soll ja auch im Formular sofort wirksam sein, AddNew akzeptiert keine Row als Parameter und AddConnectionRow funktioniert auch nicht.

    Zumal das Feld ID natürlich nicht kopiert, sondern neu vergeben werden muss (Primary Key)...

    Ich stehe da gerade irgendwie auf dem Schlauch.

    MfG. VBDev
    var coffee = new coffee();
    if(coffee.empty)
    {
    coffee.refill();
    } else {
    coffee.drink();
    }
    Du wirst keine Funktion finden, die eine bestehende DataTable-Row als Parameter akzeptiert, da es dann Meckerei gibt, weil die Row schon zu einer DataTable gehört.
    Bei DeinTds.DeineDataTable.AddDeineDataTableRow() kannst Du zwar eine Row mitgeben, aber da geht es darum, dass Du vorher eine konstruierst:

    VB.NET-Quellcode

    1. Dim NewCar = DataSet1.Cars.NewCarsRow
    2. With NewCar
    3. .Name = "FortFort"
    4. .CompanyID = 2
    5. End With
    6. DataSet1.Cars.AddCarsRow(NewCar)
    Bei stackoverflow gab es noch den Ansatz des memberwise-clonings, aber da grätscht Dir dann wieder die ID-Column rein, weil deren Wert ja unique sein muss.
    Daher mach(te) ich das so:

    VB.NET-Quellcode

    1. Dim MyCurrentCarRowAsTemplate = typisierter-Doppelcast-des-BindingSource-Current-Objektes
    2. DataSet1.Cars.AddCarsRow(MyCurrentCarRowAsTemplate.Name, MyCurrentCarRowAsTemplate.CompanyID)
    Also jede einzelne Objekt-Property als Parameter hernehmen. Denn solche AddXXXRow-Prozeduren werden ja automatisch vom tDS-Generator erzeugt.
    Man könnte ggf. noch selber eine (Extension)Prozedur schreiben, bei der man das memberwise-cloning nutzt und alle Unique/AutoIncrement-Propertys ignoriert, aber das wär mir zu aufwendig.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Den Ansatz verstehe ich, die Umsetzung klappt allerdings nicht. Das Dataset-Konzept ist da auch leicht verwirrend wenn man aus der PHP Welt kommt.

    VB.NET-Quellcode

    1. Dim duplicateRow = ConnectionBindingSource.Current.Row
    2. Connections.ConnectionDataTable.AddConnectionRow(duplicateRow.Id)


    Resultiert in fehler:

    VB.NET-Quellcode

    1. Reference to a non shared member requires an object reference


    Bei zwei Parametern:

    VB.NET-Quellcode

    1. Overload resulution failed because no accessible 'AddConnectionRow' accepts this number of arguments


    Vollzitat entfernt. ~Thunderbolt
    var coffee = new coffee();
    if(coffee.empty)
    {
    coffee.refill();
    } else {
    coffee.drink();
    }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Thunderbolt“ ()

    1. DAFUQ (Directly Appended FUll Quote). Nicht gern im Forum gesehen
    2. ConnectionBindingSource.Current.Row gibt ein Object wider. Such mal im Forum nach »Doppelcast«, damit Du statt einem Object ein Objekt vom Typ ConnectionRow herholen kannst.
    3. Connections.ConnectionDataTable.AddConnectionRow(duplicateRow.Id) Du fügst der Table eine ID hinzu? Das geht nicht. Du kannst der DataTable entweder eine vollständige Row geben oder die einzelnen Parameter. Die ID wohl garantiert als einziges nicht, wenn sie als AutoIncrement eingestellt ist*. Nutze IntelliSense. Es immer mindestens 2 Überladungen, die Du per markierten Pfeil anzeigen lassen kannst, siehe Anhang. Da wird Dir dann angezeigt, welche Parameter Du aufgrund Deines DataTable-Aufbaus übergeben musst.

    * da sich die DataTable selber um die ID-Vergabe kümmert.
    Bilder
    • IntelliSense.png

      10,39 kB, 669×133, 86 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Ähm - Bindingsource.AddNew() ist hier durchaus nützlich, weil das ist die neue Row, und die hat auch gleich den Focus.
    Also um das Problem zu lösen, mit Bindingsource.Current die alte Row holen, dann mit BindingSource.AddNew die neue Row erstellen und holen, und dann die alte Row auf die neue Row kopieren - soweit möglich (für Primkey-Spalten ists zB nicht möglich)
    Für beide ist der DoppelCast zu verwenden.
    Ich hab - um nicht immer den hässlichen Doppelcast schreiben zu müssen eine Extension geschrieben - nein sogar 2:

    VB.NET-Quellcode

    1. Public Module BindingSourceX
    2. <Diagnostics.DebuggerStepThrough()> _
    3. <Extension()> _
    4. Public Function AddNewX(Of T As DataRow)(subj As BindingSource) As T
    5. Return DirectCast(DirectCast(subj.AddNew(), DataRowView).Row, T)' der original Doppelcast!
    6. End Function
    7. ''' <summary> returnt die typisierte Datarow an aktueller Position - oder Nothing. </summary>
    8. <Extension()> _
    9. Public Function At(Of T As DataRow)(subj As BindingSource) As T
    10. Return DirectCast(subj.At(subj.Position), T)
    11. End Function
    12. ''' <summary>returnt die typisierte Datarow am index. Bei ungültigem index Nothing (keine OutOfRange-Exception!)</summary>
    13. <Extension()> _
    14. Public Function At(Of T As DataRow)(subj As BindingSource, index As Integer) As T
    15. Return DirectCast(subj.At(index), T)
    16. End Function
    17. ''' <summary>returnt die Datarow am index. Bei ungültigem index Nothing (keine OutOfRange-Exception!)</summary>
    18. <Extension()> _
    19. Public Function At(subj As BindingSource, index As Integer) As DataRow
    20. If index < 0 OrElse index >= subj.Count Then Return Nothing
    21. Return DirectCast(subj(index), DataRowView).Row
    22. End Function
    23. End Module
    Die At()-Extension ist ein kleines System - damit kann man sowohl die typRow an einem bestimmten Index abrufen, oder wenn man den Index weglässt, kriegt man die CurrentRow.
    Der Doppelcast ist dabei etwas verschleiert, da die Casts sich auf mehrere Methoden verteilen, die einander aufrufen.

    Aufrufebeispiele

    VB.NET-Quellcode

    1. dim rwPrev = ConnectionBindingSource.At(Of ConnectionRow)
    2. dim rwNew = ConnectionBindingSource.AdNewX(Of ConnectionRow)
    3. 'code zum Kopieren der einen in die annere...

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Da Du (bekanntlich) viel Wert auf korrekte Benennung legst, eine Zwischenfrage: Warum heißt Deine Extension, welche ohne Index-Parameter auskommt, auch At? At-was? Bei der Index-verlangenden verstehe ich es (»gib mir das typisierte Zeilenobjekt an Stelle x«), aber die andere? »Gib mir das typisierte Zeilenobjekt an ... öhm, ja nix weiter eben.«
    Sollte m.E. stattdessen z.B. Current heißen, also im Code

    VB.NET-Quellcode

    1. Dim rwPrev = ConnectionBindingSource.Current(Of ConnectionRow)
    da dann auch die Funktionalität klar wird. Es gibt dann einmal das normale Current, was ein Object zurückgibt, und ein typisiertes, was auch noch aus dem Object über ein DataRowView und dessen untypisierter Row eine typisierte macht. Ok, Current nenn ich die Extension in meiner Lib, ob das jetzt der perfekte Name ist, weiß ich auch nicht, aber At lässt den Coder m.E. etwas verwirrt zurück.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Jo - würde ich heute wohl auch so benamen.
    Ein gewisser Vorteil liegt darin, dasses so nur eine Extension ist, überladen.
    Ist ja lästig in vb , dass Extensions globale Modul-Methoden sind, die sich ganz unpassend in die Intellisense drängeln.
    Also je weniger Namen man mit Extensions belegt, desto besser (dachte ich)

    Achso - und bs.Current kannste die Extension nicht benennen, weil BindingSource hat eine ObjektProperty dieses Namens - die zieht der Compiler einer Extension vor.
    Eh, doch, weiß ich, kann ich und nutz ich seit langer Zeit. Klar, man muss immer Current(Of bla) schreiben, um die richtige Funktionalität zu erwischen. Aber das muss man ja eh, um es typisiert zu machen. Also zusammengefasst: es geht problemlos.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.