Benutzer mit den meisten Antworten
DatagridView, SQL-Tabelle über BindingSource dargestellt: berechnete Datenfelder (SQL-Tab.) werden nicht angezeigt

Frage
-
Hallo zusammen,
ich habe eine SQL-Tabelle mit drei berechneten Spalten, also Datenfelder, die mittels einer Formel durch SQL selbst berechnet werden, wenn sich ein Wert in der Formel ändert.
Auch diese Felder werden im DGV mittels BindingSource (gefilter) dargestellt. Das funktioniert beim ersten Anzeigen des DGV. Wenn ich aber nun neue Datensätze hinzufüge (übers DGV) und sie über DatAdapter update, werden diese berechneten Werte nicht sofort angezeigt, sondern nur nach erneutem Einlesen der Tabelle und verbinden mit dem DGV.Wie kann man erreichen, dass die berechneten Spalten, also deren Werte, bei neuem Datensatz gleich gezeigt werden?
Grüße-
Dietrich
Antworten
-
Hi Dietrich,
die Idee ist folgende. Du hängst an das RowUpdated-Ereignis des DataAdapters eine Methode, die das Zurücklesen ausführt:AddHandler <DataAdapter>.RowUpdated, AddressOf Rowupdated
Die Routine wird für jeden Datensatz aufgerufen, der in die Datenbank geschrieben wird (INSERT oder UPDATE). In der Routine liest Du einfach den Datensatz zurück und überschreibst die gewünschten Feldinhalte
Private Shared Sub Rowupdated(ByVal sender As Object, _ ByVal e As SqlRowUpdatedEventArgs) If e.StatementType = StatementType.Insert Then Dim cmd As New SqlCommand("SELECT * FROM Tab1 WHERE ID = SCOPE_IDENTITY()", cn) cmd.Transaction = e.Command.Transaction Dim rdr = cmd.ExecuteReader if rdr.Read Then e.Row("Feldname") = rdr("Feldname") End If End Sub
Eine andere Möglichkeit besteht im Anhängen des Rücklesens an das INSERT:
Private Shared da1 As System.Data.SqlClient.SqlDataAdapter Private Shared ds As New DataSet Public Shared Sub LoadData() cn = New System.Data.SqlClient.SqlConnection(ConnectionString) Dim cmd1 As New System.Data.SqlClient.SqlCommand("", cn) cmd1.CommandText = "SELECT *, Cast(Convert(nchar(8), Datum, 101)" & _ " + ' ' + Tageszeit As datetime) As Zeit FROM xTab1" da1 = New System.Data.SqlClient.SqlDataAdapter(cmd1) With da1 .MissingSchemaAction = MissingSchemaAction.AddWithKey .FillLoadOption = LoadOption.Upsert .Fill(ds, "Tab1") End With With ds.Tables("Tab1").Columns("ID") .AutoIncrement = True .AutoIncrementStep = -1 .AutoIncrementSeed = -1 End With With bs1 .DataSource = ds .DataMember = "Tab1" End With End Sub Private Shared bs1 As New BindingSource Public Shared Function GetBindindSource1() As BindingSource Return bs1 End Function Public Shared Sub UpdateData() Dim cb1 As New System.Data.SqlClient.SqlCommandBuilder(da1) bs1.EndEdit() Dim r() As DataRow Try ' New Row's r = ds.Tables("Tab1").Select(Nothing, Nothing, DataViewRowState.Added) If r.Length > 0 Then With da1 .InsertCommand = cb1.GetInsertCommand.Clone With .InsertCommand .UpdatedRowSource = UpdateRowSource.FirstReturnedRecord .CommandText &= ";SELECT * FROM xTab1 WHERE (ID = SCOPE_IDENTITY())" End With .Update(r) End With End If da1.Update(ds.Tables("Tab1")) Catch ex As Exception Trace.WriteLine(ex.ToString) End Try End Sub
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort markiert dherrmann Donnerstag, 30. November 2017 16:27
-
Hallo Peter, nochmals vielen Dank für die Tipps!
Mittlerweile verwende ich die von dir gezeigte 2. Methode. Ich habe dazu folgende Funktion in Anwendung:
''' <summary> ''' Updaten Dataset SQL (inkl. Re-Read-Funktion) ''' </summary> ''' <param name="dSet">das Dataset</param> ''' <param name="bs">die BindingSource</param> ''' <param name="da">der SQLDataAdapter</param> ''' <param name="n">der Tabellenname</param> ''' <param name="idName">der Name der Schlüsselspalte, wenn Re-Read gewünscht</param> Public Sub dataUpdate(dSet As DataSet, bs As BindingSource, da As SqlDataAdapter, n As String, Optional idName As String = "") bs.EndEdit() Try ' neue Zeilen If idName <> "" Then ' nur angegeben für Tabellen mit Re-Read-Funktion Dim ccb As New SqlCommandBuilder(da) Dim r() As DataRow r = dSet.Tables(n).Select(Nothing, Nothing, DataViewRowState.Added) If r.Length > 0 Then With da .InsertCommand = ccb.GetInsertCommand.Clone With .InsertCommand .UpdatedRowSource = UpdateRowSource.FirstReturnedRecord .CommandText += ";SELECT * FROM " + n _ + " WHERE (" + idName + " = SCOPE_IDENTITY())" End With .Update(r) End With End If End If da.Update(dSet.Tables(n)) Catch ex As Exception bs.CancelEdit() MessageBox.Show(ex.Message, "RE-READ") End Try End Sub
Funktioniert!
Viele Grüße-
Dietrich
- Als Antwort markiert dherrmann Donnerstag, 30. November 2017 16:27
Alle Antworten
-
Hi Dietrich,
da der SQL Server die Feldinhalte berechnet, musst Du bei Neuanlage eines Datensatzes diesen zurücklesen. Das kannst direkt nach den INSERT machen oder auch ein AddWithKey nutzen. Am einfachsten ist es sich in das entsprechende Ereignis des DataAdapters zu hängen und nach dem Schreiben eines neuen Datensatzes diesen zurücklesen und damit den neu erfassten Datensatz überschreiben.--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Danke, Peter, für die schnelle Antwort!
was du vorschlägst, würde ich gern ausprobieren... Doch vielleicht kannst du mir noch ein wenig auf die Sprünge helfen.
Ich mache bspw. vor dem Füllen des DataSets mit der Tabelledatadapter.MissingSchemaAction = MissingSchemaAction.AddWithKey (SQLDataAdapter))
Welches DataAdapter-Ereignis meinst du? Ich update mit
dataadapter.Update(dataset, tabellenname)
meinst du, dass ich nach dieser Aktion das Zurücklesen machen soll? Allerdings: mit welchem VB-Befehl?Danke im Voraus!
Grüße-
Dietrich
- Bearbeitet dherrmann Mittwoch, 29. November 2017 20:26
-
Hi Dietrich,
die Idee ist folgende. Du hängst an das RowUpdated-Ereignis des DataAdapters eine Methode, die das Zurücklesen ausführt:AddHandler <DataAdapter>.RowUpdated, AddressOf Rowupdated
Die Routine wird für jeden Datensatz aufgerufen, der in die Datenbank geschrieben wird (INSERT oder UPDATE). In der Routine liest Du einfach den Datensatz zurück und überschreibst die gewünschten Feldinhalte
Private Shared Sub Rowupdated(ByVal sender As Object, _ ByVal e As SqlRowUpdatedEventArgs) If e.StatementType = StatementType.Insert Then Dim cmd As New SqlCommand("SELECT * FROM Tab1 WHERE ID = SCOPE_IDENTITY()", cn) cmd.Transaction = e.Command.Transaction Dim rdr = cmd.ExecuteReader if rdr.Read Then e.Row("Feldname") = rdr("Feldname") End If End Sub
Eine andere Möglichkeit besteht im Anhängen des Rücklesens an das INSERT:
Private Shared da1 As System.Data.SqlClient.SqlDataAdapter Private Shared ds As New DataSet Public Shared Sub LoadData() cn = New System.Data.SqlClient.SqlConnection(ConnectionString) Dim cmd1 As New System.Data.SqlClient.SqlCommand("", cn) cmd1.CommandText = "SELECT *, Cast(Convert(nchar(8), Datum, 101)" & _ " + ' ' + Tageszeit As datetime) As Zeit FROM xTab1" da1 = New System.Data.SqlClient.SqlDataAdapter(cmd1) With da1 .MissingSchemaAction = MissingSchemaAction.AddWithKey .FillLoadOption = LoadOption.Upsert .Fill(ds, "Tab1") End With With ds.Tables("Tab1").Columns("ID") .AutoIncrement = True .AutoIncrementStep = -1 .AutoIncrementSeed = -1 End With With bs1 .DataSource = ds .DataMember = "Tab1" End With End Sub Private Shared bs1 As New BindingSource Public Shared Function GetBindindSource1() As BindingSource Return bs1 End Function Public Shared Sub UpdateData() Dim cb1 As New System.Data.SqlClient.SqlCommandBuilder(da1) bs1.EndEdit() Dim r() As DataRow Try ' New Row's r = ds.Tables("Tab1").Select(Nothing, Nothing, DataViewRowState.Added) If r.Length > 0 Then With da1 .InsertCommand = cb1.GetInsertCommand.Clone With .InsertCommand .UpdatedRowSource = UpdateRowSource.FirstReturnedRecord .CommandText &= ";SELECT * FROM xTab1 WHERE (ID = SCOPE_IDENTITY())" End With .Update(r) End With End If da1.Update(ds.Tables("Tab1")) Catch ex As Exception Trace.WriteLine(ex.ToString) End Try End Sub
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks- Als Antwort markiert dherrmann Donnerstag, 30. November 2017 16:27
-
Zunächst herzlichen Dank für die Tipps, Peter!
Bis zum AddHandler für OnRowUpdated bin ich schon gekommen, aber dann wusste ich nicht so recht weiter.
Vorab: cn ist die Connection?
Feldname ist der Name des berechneten Feldes, oder? Und wenn es mehrere berechnete Felder sind?
Deinen Code werde ich Morgen ausprobieren und melde mich danach hier...
Beste Grüße-
Dietrich
- Bearbeitet dherrmann Mittwoch, 29. November 2017 21:16
-
Hi Dietrich,
cn ist die Verweisvariable auf das Connection-Objekt,Feldname ist der Name der Spalte, die zurückzulesen ist. Wenn es mehrere sind, dann muss die Anweisung für jeden Feldnamen im If-Block ausgeführt werden.
--
Viele Grüsse
Peter Fleischer (ehem. MVP)
Meine Homepage mit Tipps und Tricks -
Hallo Peter,
beim ersten Test verwende ich folgenden Code:
Private Sub daReis_RowUpdated(sender As Object, e As SqlRowUpdatedEventArgs) _ Handles daReis.RowUpdated If e.StatementType = StatementType.Insert Then Dim cmd As New SqlCommand("SELECT * FROM Reisekosten WHERE ReisID = SCOPE_IDENTITY()", connection) cmd.Transaction = e.Command.Transaction Dim rdr = cmd.ExecuteReader If rdr.Read Then e.Row("StundenGesamt") = rdr("StundenGesamt") e.Row("Tage") = rdr("Tage") e.Row("Taggeld") = rdr("Taggeld") End If End If End Sub
Beim Debuggen sehe ich, dass cmd.Transaction nach Ausführung von cmd.Transaction = cmd.ExecuteReader gleich Nothing ist. Dann geht natürlich nichts weiter.
Übrigens ist ReisID der Primärschlüssel der Tabelle.
Was kann da falsch sein?Grüße-
Dietrich
- Bearbeitet dherrmann Donnerstag, 30. November 2017 10:01
-
Es liegt zunächst am
SCOPE_IDENTITY()
Wenn ich die letzte Schlüsselnummer direkt einsteuere, kommt es zumindest zum Lesen des Datensatzes.
Allerdings taucht dann ein weiteres Problem auf:
Die Spalte "StundenGesamt" ist schreibgeschützt wird mitgeteilt! Was logisch ist, denn es ist ja eine berechnete Spalte. Dies gilt auch für die anderen beiden Spalten.Jetzt weiß ich nicht weiter...Grüße-
Dietrich
-
Hallo Peter, nochmals vielen Dank für die Tipps!
Mittlerweile verwende ich die von dir gezeigte 2. Methode. Ich habe dazu folgende Funktion in Anwendung:
''' <summary> ''' Updaten Dataset SQL (inkl. Re-Read-Funktion) ''' </summary> ''' <param name="dSet">das Dataset</param> ''' <param name="bs">die BindingSource</param> ''' <param name="da">der SQLDataAdapter</param> ''' <param name="n">der Tabellenname</param> ''' <param name="idName">der Name der Schlüsselspalte, wenn Re-Read gewünscht</param> Public Sub dataUpdate(dSet As DataSet, bs As BindingSource, da As SqlDataAdapter, n As String, Optional idName As String = "") bs.EndEdit() Try ' neue Zeilen If idName <> "" Then ' nur angegeben für Tabellen mit Re-Read-Funktion Dim ccb As New SqlCommandBuilder(da) Dim r() As DataRow r = dSet.Tables(n).Select(Nothing, Nothing, DataViewRowState.Added) If r.Length > 0 Then With da .InsertCommand = ccb.GetInsertCommand.Clone With .InsertCommand .UpdatedRowSource = UpdateRowSource.FirstReturnedRecord .CommandText += ";SELECT * FROM " + n _ + " WHERE (" + idName + " = SCOPE_IDENTITY())" End With .Update(r) End With End If End If da.Update(dSet.Tables(n)) Catch ex As Exception bs.CancelEdit() MessageBox.Show(ex.Message, "RE-READ") End Try End Sub
Funktioniert!
Viele Grüße-
Dietrich
- Als Antwort markiert dherrmann Donnerstag, 30. November 2017 16:27