Benutzer mit den meisten Antworten
Problem mit LINQtoSQL

Frage
-
Hallo liebe Gemeinde,
ich habe gerade ein kleines Problem und kann den Fehler nicht finden: ich habe eine Form mit einem DataGridView. Ich frage über LINQ Daten aus der Datenbank ab und zeige sie im Grid an. Jetzt möchte ich dem Nutzer die Möglichkeit bieten, einen Datensatz zu kopieren/auszuschneiden und wieder einzufügen. Dazu habe ich folgenden Code geschrieben:
Public Class frmBenutzerList ' Deklaration der Benutzerklasse (LINQ) Dim b As Benutzer ''' <summary> ''' Legt eine Kopie des Datensatzes an (Instanz der Benutzerklasse) ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub CopyToClipboard(ByVal sender As System.Object, ByVal e As System.EventArgs) ' Referenzieren des Elternfensters Dim frmParent = TryCast(Me.MdiParent, frmMain) If frmParent IsNot Nothing Then ' Prüfen, ob von b schon eine Instanz existiert, sonst ... If b Is Nothing Then ' ... Anlegen des neuen Benutzers b = New Benutzer() End If ' Einfüllen der Daten (evtl. überschreiben) For Each r As DataGridViewRow In ergebnisDataGridView.SelectedRows 'b.ID = CInt(r.Cells(0).Value) b.Benutzername = r.Cells(1).Value.ToString() b.Passwort = r.Cells(2).Value.ToString() b.Vorname = r.Cells(3).Value.ToString() b.Nachname = r.Cells(4).Value.ToString() b.aktiv = CBool(r.Cells(5).Value) Next ' Einfügen-Buttons aktivieren frmParent.EinfuegenToolStripButton.Enabled = True frmParent.EinfuegenToolStripMenuItem.Enabled = True End If End Sub ''' <summary> ''' Ausschneiden des ausgewählten Benutzers ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub CutToClipBoard(ByVal sender As System.Object, ByVal e As System.EventArgs) ' Kopierfunktion nutzen, um eine Kopie des Benutzers anzulegen CopyToClipboard(sender, e) ' Instanz des DataContexts Dim db As New basarDataContext(My.Settings.basarConnectionString) ' Variable zum Speichern der ID Dim ii As Integer = 0 For Each r As DataGridViewRow In ergebnisDataGridView.SelectedRows ' Die ID aus dem Grid ermitteln ii = CInt(r.Cells(0).Value) Next ' Abfragen des richtigen Datensatzes Dim q = (From b In db.Benutzers _ Where b.ID = ii _ Select b).Single() ' Benutzer inaktiv setzen q.aktiv = False ' Änderungen an die Datenbank senden db.SubmitChanges() ' Aufräumen db.Dispose() ' Daten neu laden (Refresh) LadeDaten() End Sub ''' <summary> ''' Fügt die vorher kopierte/ausgeschnittene Benutzerinstanz in die Datenbank ein. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub PasteToDatabase(ByVal sender As System.Object, ByVal e As System.EventArgs) ' Referenzieren des Elternfensters Dim frmParent = TryCast(Me.MdiParent, frmMain) If frmParent IsNot Nothing Then ' Instanz des DataContexts Dim db As New basarDataContext(My.Settings.basarConnectionString) ' Anhängen des Benutzers an den Context db.Benutzers.Attach(b) ' Den Benutzer in die Datenbank schreiben db.SubmitChanges() ' Aufräumen db.Dispose() ' Daten neu laden LadeDaten() End If End Sub ''' <summary> ''' Lädt die Benutzerliste in das Grid. ''' </summary> ''' <remarks></remarks> Friend Sub LadeDaten() ' Referenzieren des Elternfensters Dim frmParent = TryCast(Me.MdiParent, frmMain) If frmParent IsNot Nothing Then ' Instanz des DataContexts Dim db As New basarDataContext(My.Settings.basarConnectionString) ' Abfragen der Daten Dim q = From b In db.Benutzers _ Where b.aktiv = True _ Select b ' Die abgefragten Daten an das Grid binden Me.ergebnisDataGridView.DataSource = q.ToList() ' Aufräumen db.Dispose() q = Nothing End If End Sub End Class
Die Instanz von b wird sowohl bei der Kopierfunktion, als auch bei der Ausschneidenfunktion gefüllt. Aber das Einfügen klappt nicht (PasteToDatabase()). Könnt Ihr den Fehler erkennen? Tausend Dank im Voraus für Eure Hilfe!
Gruß
Marcus
Der erste Tag, an dem ich nichts Neues lerne, wird der Tag sein, an dem sich der Deckel über mir schließt...
Antworten
-
Hallo Marcus,
Kurzfassung: Hier wäre InsertOnSubmit anstatt Attach zu verwenden,
siehe Vorgehensweise: Einfügen von Zeilen in die Datenbank (LINQ to SQL)Einige Anmerkungen / Vorschläge zu Deinem Code:
Anstatt sich mit den Cells rumzuschlagen, verwende das DataBoundItem
Dann hast Du eine typisierte Version und der Code wird klarer und auch nicht so anfällig füreine andere Spaltenreihenfolge uam.Auf solche Dinge wie das Weiternuten eines Benutzers würde ich verzichten,
eine neue Instanz tut nicht weh, eher sie hier nach dem Einfügen weiterleben zu lassen.Im übrigen würde ich Dir empfehlen, den Datenzugriff auszulagern.-
Schon bei mir hat das Nachverfolgen um einiges länger gedauert, Dir dürfte es nach einige Wochen / Monaten nicht anders gehen.
Besser ist es man vermischt nie Oberfläche und Datenzugriff, so kann man den Datenzugriff unabhängig prüfen und testen
und erfahrungsgemäss entfällt damit auch einiger doppelt geschriebener / ähnlicher Code (der sich ansonsten in diversen Formularen versteckt).Als Vorschlag:
Public Class BenutzerList Dim benutzerService As New BenutzerDataService() ' Aktuelle Kopie Dim benutzerClipboard As Benutzer ''' <summary>Legt eine Kopie des Datensatzes an (Instanz der Benutzerklasse)</summary> Friend Sub CopyToClipboard(ByVal sender As System.Object, ByVal e As System.EventArgs) ' Referenzieren des Elternfensters If CanCutOrCopy() Then Dim benutzerAuswahl = TryCast(ergebnisDataGridView.SelectedRows(0).DataBoundItem, Benutzer) benutzerClipboard = benutzerService.BenutzerKopieren(benutzerAuswahl) ' TODO: Sollte auch deaktiviert werden ' Einfügen-Buttons aktivieren 'frmParent.EinfuegenToolStripButton.Enabled = True 'frmParent.EinfuegenToolStripMenuItem.Enabled = True End If End Sub ''' <summary>Ausschneiden des ausgewählten Benutzers</summary> Friend Sub CutToClipBoard(ByVal sender As System.Object, ByVal e As System.EventArgs) If CanCutOrCopy() Then Dim benutzerAuswahl = TryCast(ergebnisDataGridView.SelectedRows(0).DataBoundItem, Benutzer) benutzerClipboard = benutzerService.BenutzerKopieren(benutzerAuswahl) benutzerService.SetzeBenutzerInaktiv(benutzerAuswahl.ID) LadeDaten() End If End Sub ''' <summary>Fügt die vorher kopierte/ausgeschnittene Benutzerinstanz in die Datenbank ein.</summary> Friend Sub PasteToDatabase(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim frmParent = TryCast(Me.MdiParent, frmMain) ' Hier Benutzervariable prüfen If frmParent IsNot Nothing AndAlso benutzerClipboard IsNot Nothing Then benutzerService.BenutzerEinfügen(benutzerClipboard) ' Sonst gibt sie womöglich später doppelt benutzerClipboard = Nothing LadeDaten() End If End Sub ''' <summary>Lädt die Benutzerliste in das Grid.</summary> Friend Sub LadeDaten() ' Referenzieren des Elternfensters Dim frmParent = TryCast(Me.MdiParent, frmMain) If frmParent IsNot Nothing Then Me.ergebnisDataGridView.DataSource = benutzerService.LadeAktiveBenutzer() End If End Sub ''' <summary>Liefert True, wenn kopiert oder ausgeschnitten werden kann</summary> Private Function CanCutOrCopy() As Boolean Dim frmParent = TryCast(Me.MdiParent, frmMain) ' Parent und gültige Auswahl (Kein Multi Select) If frmParent IsNot Nothing AndAlso ergebnisDataGridView.SelectedRows.Count = 1 Then Return True End If Return False End Function End Class ''' <summary>Führt die Operationen für Benutzer aus.</summary> Public Class BenutzerDataService ' Durch basarDataContext (und Verbindungszeichenfolge ersetzen) Private Function GetContext() As NorthwindDataContext Return New NorthwindDataContext() End Function ''' <summary>Lädt alle aktiven Benutzer</summary> Public Function LadeAktiveBenutzer() As IList(Of Benutzer) Using db = GetContext() Dim q = From b In db.Benutzers _ Where b.Aktiv = True _ Select b Return q.ToList() End Using End Function ''' <summary>Fügt einen neuen Benutzer ein.</summary> Public Sub BenutzerEinfügen(entity As Benutzer) Using db = GetContext() db.Benutzers.InsertOnSubmit(entity) db.SubmitChanges() End Using End Sub ''' <summary>Setzt den Benutzer mit der ID auf Inaktiv</summary> ''' <remarks>Wenn beide Richtungen mit boolschen Parameter</remarks> Public Sub SetzeBenutzerInaktiv(id As Integer) Using db = GetContext() Dim benutzer = (From b In db.Benutzers _ Where b.ID = id Select b).SingleOrDefault() If benutzer IsNot Nothing Then benutzer.Aktiv = False db.SubmitChanges() End If End Using End Sub ''' <summary>Erstellt eine Kopie eines Benutzer (ohne Primärschlüssel)</summary> Public Function BenutzerKopieren(entity As Benutzer) As Benutzer Dim b = New Benutzer() b.Benutzername = entity.Benutzername & " Neu" b.Passwort = entity.Passwort b.Vorname = entity.Vorname b.Nachname = entity.Nachname b.Aktiv = entity.Aktiv Return b End Function End Class
Nicht über den NorthwindDataContext wundern, den hatte ich der Kürze halber verwendet,
und durch Deinen Context austauschen (ist jetzt nur noch die eine zeile). LadeDaten müsste nicht immer aufgerufen werden, man kann auch den Benutzer aus der Auflistung rauswerfen.
Da es aber das Verhalten ändern würde, habe ich es so belassen.
Gruß Elmar
- Als Antwort markiert mjanz Donnerstag, 4. August 2011 18:57
Alle Antworten
-
Hallo Marcus,
Kurzfassung: Hier wäre InsertOnSubmit anstatt Attach zu verwenden,
siehe Vorgehensweise: Einfügen von Zeilen in die Datenbank (LINQ to SQL)Einige Anmerkungen / Vorschläge zu Deinem Code:
Anstatt sich mit den Cells rumzuschlagen, verwende das DataBoundItem
Dann hast Du eine typisierte Version und der Code wird klarer und auch nicht so anfällig füreine andere Spaltenreihenfolge uam.Auf solche Dinge wie das Weiternuten eines Benutzers würde ich verzichten,
eine neue Instanz tut nicht weh, eher sie hier nach dem Einfügen weiterleben zu lassen.Im übrigen würde ich Dir empfehlen, den Datenzugriff auszulagern.-
Schon bei mir hat das Nachverfolgen um einiges länger gedauert, Dir dürfte es nach einige Wochen / Monaten nicht anders gehen.
Besser ist es man vermischt nie Oberfläche und Datenzugriff, so kann man den Datenzugriff unabhängig prüfen und testen
und erfahrungsgemäss entfällt damit auch einiger doppelt geschriebener / ähnlicher Code (der sich ansonsten in diversen Formularen versteckt).Als Vorschlag:
Public Class BenutzerList Dim benutzerService As New BenutzerDataService() ' Aktuelle Kopie Dim benutzerClipboard As Benutzer ''' <summary>Legt eine Kopie des Datensatzes an (Instanz der Benutzerklasse)</summary> Friend Sub CopyToClipboard(ByVal sender As System.Object, ByVal e As System.EventArgs) ' Referenzieren des Elternfensters If CanCutOrCopy() Then Dim benutzerAuswahl = TryCast(ergebnisDataGridView.SelectedRows(0).DataBoundItem, Benutzer) benutzerClipboard = benutzerService.BenutzerKopieren(benutzerAuswahl) ' TODO: Sollte auch deaktiviert werden ' Einfügen-Buttons aktivieren 'frmParent.EinfuegenToolStripButton.Enabled = True 'frmParent.EinfuegenToolStripMenuItem.Enabled = True End If End Sub ''' <summary>Ausschneiden des ausgewählten Benutzers</summary> Friend Sub CutToClipBoard(ByVal sender As System.Object, ByVal e As System.EventArgs) If CanCutOrCopy() Then Dim benutzerAuswahl = TryCast(ergebnisDataGridView.SelectedRows(0).DataBoundItem, Benutzer) benutzerClipboard = benutzerService.BenutzerKopieren(benutzerAuswahl) benutzerService.SetzeBenutzerInaktiv(benutzerAuswahl.ID) LadeDaten() End If End Sub ''' <summary>Fügt die vorher kopierte/ausgeschnittene Benutzerinstanz in die Datenbank ein.</summary> Friend Sub PasteToDatabase(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim frmParent = TryCast(Me.MdiParent, frmMain) ' Hier Benutzervariable prüfen If frmParent IsNot Nothing AndAlso benutzerClipboard IsNot Nothing Then benutzerService.BenutzerEinfügen(benutzerClipboard) ' Sonst gibt sie womöglich später doppelt benutzerClipboard = Nothing LadeDaten() End If End Sub ''' <summary>Lädt die Benutzerliste in das Grid.</summary> Friend Sub LadeDaten() ' Referenzieren des Elternfensters Dim frmParent = TryCast(Me.MdiParent, frmMain) If frmParent IsNot Nothing Then Me.ergebnisDataGridView.DataSource = benutzerService.LadeAktiveBenutzer() End If End Sub ''' <summary>Liefert True, wenn kopiert oder ausgeschnitten werden kann</summary> Private Function CanCutOrCopy() As Boolean Dim frmParent = TryCast(Me.MdiParent, frmMain) ' Parent und gültige Auswahl (Kein Multi Select) If frmParent IsNot Nothing AndAlso ergebnisDataGridView.SelectedRows.Count = 1 Then Return True End If Return False End Function End Class ''' <summary>Führt die Operationen für Benutzer aus.</summary> Public Class BenutzerDataService ' Durch basarDataContext (und Verbindungszeichenfolge ersetzen) Private Function GetContext() As NorthwindDataContext Return New NorthwindDataContext() End Function ''' <summary>Lädt alle aktiven Benutzer</summary> Public Function LadeAktiveBenutzer() As IList(Of Benutzer) Using db = GetContext() Dim q = From b In db.Benutzers _ Where b.Aktiv = True _ Select b Return q.ToList() End Using End Function ''' <summary>Fügt einen neuen Benutzer ein.</summary> Public Sub BenutzerEinfügen(entity As Benutzer) Using db = GetContext() db.Benutzers.InsertOnSubmit(entity) db.SubmitChanges() End Using End Sub ''' <summary>Setzt den Benutzer mit der ID auf Inaktiv</summary> ''' <remarks>Wenn beide Richtungen mit boolschen Parameter</remarks> Public Sub SetzeBenutzerInaktiv(id As Integer) Using db = GetContext() Dim benutzer = (From b In db.Benutzers _ Where b.ID = id Select b).SingleOrDefault() If benutzer IsNot Nothing Then benutzer.Aktiv = False db.SubmitChanges() End If End Using End Sub ''' <summary>Erstellt eine Kopie eines Benutzer (ohne Primärschlüssel)</summary> Public Function BenutzerKopieren(entity As Benutzer) As Benutzer Dim b = New Benutzer() b.Benutzername = entity.Benutzername & " Neu" b.Passwort = entity.Passwort b.Vorname = entity.Vorname b.Nachname = entity.Nachname b.Aktiv = entity.Aktiv Return b End Function End Class
Nicht über den NorthwindDataContext wundern, den hatte ich der Kürze halber verwendet,
und durch Deinen Context austauschen (ist jetzt nur noch die eine zeile). LadeDaten müsste nicht immer aufgerufen werden, man kann auch den Benutzer aus der Auflistung rauswerfen.
Da es aber das Verhalten ändern würde, habe ich es so belassen.
Gruß Elmar
- Als Antwort markiert mjanz Donnerstag, 4. August 2011 18:57
-
Hallo Elmar,
erstmal tausend Dank für Deine Antwort. Wie immer klasse erklärt! Muss ich mich eigentlich schämen, dass ich nicht auf so etwas komme? ;-) Danke nochmal!
Gruß
Marcus
Der erste Tag, an dem ich nichts Neues lerne, wird der Tag sein, an dem sich der Deckel über mir schließt...