TableAdapter.Update() - Fehler bei Änderung

  • VB.NET

Es gibt 27 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    TableAdapter.Update() - Fehler bei Änderung

    Hallo!

    Ich versuche mich gerade an der Datenbankanbindung mit DataSet/BindingSource/TableAdapter.

    Ich habe mit dem Assistenten eine Verbindung zu einer Access Datenbank hergestellt.
    Um dieses zu testen, habe ich noch eine Textbox eingefügt und diese an eine Tabelle gebunden...funktioniert auch alles.

    Sobald ich aber einen Datensatz ändere und auf meinen Button drücke, bekomme ich den Fehler:
    Ein Ausnahmefehler des Typs "System.InvalidOperationException" ist in System.Data.dll aufgetreten.
    Zusätzliche Informationen: Für ein Update ist ein gültiger UpdateCommand erforderlich, wenn eine DataRow-Auflistung mit modifizierten Zeilen weitergegeben wird.

    Code vom Button:

    Quellcode

    1. EinstellungenBindingSource.EndEdit()
    2. EinstellungenTableAdapter.Update(ConfigDataSet.AppEinstellungen)


    Ich habe mal gegoogelt und in mein Buch nachgesehen, jedoch finde ich keinen Unterschied vom Aufbau...

    Muss ich vorher noch etwas machen? Oder doch ein Gedankenfehler?
    Wie gesagt: In der Datenbank muss der Primärschlüssel eingerichtet sein. Nur ein Index reicht nicht - Primärschlüssel.

    Und wenn du das nachträglich einrichtest, dann musst du den Assistenten zum Erstellen des typisierten Datasets nochmal abfahren, damit die TableAdapter neu und richtig konfiguriert werden.
    Das Tehma interessiert mich auch.

    Ich habe ein paar grundsätzliche Fragen dazu.

    Wenn man das oben beschriebene Projekt realisiert befindet sich in der Form:
    - ein DataSet
    - eine BindingSource
    - ein Table Adapter
    - unde ien TableAdapterManager

    Wenn ich das richtig verstehe sorgt der TableAdapter meiner Db für ein Abbild im Speicher mit dem ich arbeite. Bedeutet das ich bei MeineDbTableAdapter.insert() die Daten erst in den Adapter schreibe und ihm dann sagen muss MeineDbTableAdapter.Update(MeineDbDataSet.MeineDb) damit die Daten auch in die Datenbank gelangen.
    Bei einem Windows Forms Projekt ist das unproblematisch wie ich finde. Da man mit Habwissen es schafft Daten in die DB zu schrieben. Anders sieht das bei einem Konsolenprojekt aus. Hierfür würde mich das theoretische Konstruk interessieren. Wie hängen die oben genannten Strukturen zusammen?

    TableAdapterManager verwaltet den TableAdapter mit dem ich kommuniziere. Dieser wiederum wir in einem DataSet abgebildet? Und was macht die BindingSource?

    Bis gestern dachte ich noch das Datenbankverbindungen kein hexenwerks sind und lediglich etwas Code benötigen. Als ich auf dieses erfinderdesrades.unitweb.de/Pr…r/Downloadpage/index.html Tutorial gestoßen bin habe ich mich mit SQL und dem Datenbankdesigner in Visual Studio beschäftigt und jetzt funktioniert nichts mehr. Dabei dachte ich das ich mir so viel mehr Komfort damit erschaffe :)

    Gruß
    Ganz grob erklärt:
    Eine DatenBank liegt ausserhalb deiner Anwendung und ein DataSet befindet sich im Speicher deines Rechners!
    Das Dataset hat Tabellen, die von deren Tableadaptern mit Daten versorgt werden.

    Mit den Funktionen des TableAdapters können die Daten persistiert werden.
    .Fill-Funktion befüllt die Tabelle des Dataset's mit Daten von der DB...
    .Update-Funktion speichert die geänderten Daten wieder zurrück zur DB...
    Der TableAdapterManager beinhaltet und verwaltet alle TableAdapter und bedient sie über eine Verbindungszeichenfolge als Connection zur DB.

    Die Bindingsources der einzelnen Tabellen sorgen für eine direkte Verbindung zur Anzeigeschicht deiner Anwendung.
    Tabelle <--> Bindingsource <--> z.b. ein DGV (dient nur zur Visualisierung der Daten)

    Z.B. ein DGV zeigt die Daten tabellarisch an. Man kann Datenzeilen dort bearbeiten.
    Die bearbeiteten Datenzeilen werden via Bindigsource sofort in der Tabelle deines Datasets übernommen...

    Daten-Operationen sollten nicht in der Anzeigeschicht (DGV) sondern tiefer unten entweder mit Hilfe der Bindingsource oder gleich in der Datenschicht (Tabelle) erfolgen.

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

    Edit: Ups - viel zu spät - naja egal

    Ich setze mal voraus, dass du weißt, dass die Struktur einer Datenbank sich aus mehreren Tabellen und Relationen zwischen den Tabellen zusammensetzt, und die Tabellen haben viele Spalten, je unterschiedlichen Datentyps.
    Falls du das jetzt nicht einfach abnicken kannst: gugge die relationale GrundIdee

    DAs Dataset ist nun ein Daten-Cache, der strukturell genau so aufgebaut sein muss wie die DB.
    Dann kann ein TableAdapter Daten aus der DB in den Cache laden (also ins Dataset), und ebenso im Dataset geänderte Daten zurückschreiben in die DB.

    Ein TableAdapter ist ein Adapter, ein Verbindungsstück. Da schreibt man nix hinein. Sondern man schreibt ins Dataset - wenn überhaupt.

    Denn hier gibts einen 2. Adapter, der das Dataset mit den Controls verbindet, etwa mit einem Datagridview. Dieser Adapter heißt BindingSource, und bindet zB ein DGV an eine DataTable (im Dataset, nicht inne DB).
    Die Vorgänge, die über BindingSource vermittelt zwischen DGV und Dataset-Tabelle ablaufen, heißen Databinding.
    Mittels Databinding schreibst du also auch nichts ins Dataset, sondern bindest es ans DGV, und was der User dann ins DGV tippt, ist per Databinding gleichzeitig ins Dataset geschrieben.

    Hingegen der TableAdapter verbindet nicht in Echtzeit mit der DB, sondern der wird explizit angewiesen, das Dataset zu befüllen, oder in Gegenrichtung die DB zu updaten.

    Der TableadapterManager soll das ganze noch weiter vereinfachen, denn darin sind mehrere TableAdapter enthalten - für jede DataTable einer
    Sodass man nur noch TableAdapterManager.Update aufrufen muss, und der updatet alle Tabellen gleichzeitig.

    So, nun zu Konsole: Da gibts ja keine Controls, und deshalb gibts auch keine BindingSource. Und es gibt auch keinen FormDesigner, der die TableAdapter generiert, und den TableAdapterManager damit richtig konfiguriert.
    Danke für eure Antworten. Die Erklärungen sind euch gut gelungen. Was Datenbanken sind und wie sie generell funktionieren ist mir klar (Vorleung Datenbanksysteme) leider scheitert es an der Praxis.

    Nachdem mir jetzt die Struktur klar ist frag ich mich wie ich das umsetzten kann.

    Da ich ja kein DGV habe kann ich die Daten doch direkt in mein DatatSet schreiben und durch den Table Adapter das Comit in die DB anstoßen oder sehe ich das falsch?

    Theoretischer Projektaufbau:
    - Konsolenanwendung
    - Datenquelle einfügen (Lokal) incl Datatset
    - Im Datenbankexplorer eine DB erstellen

    Dann liegen im Projektexplorer Programm.vb, Datenbank.mdf und DatenbankDatatSet.xsd

    In meiner Programm.vb kann ich jetzt:

    VB.NET-Quellcode

    1. Class DatenbankTest
    2. Sub Test
    3. Dim MeineDatenbankTableAdapter As New DatenbankDataSetTableAdapter.MeineDatenbankTableAdapter
    4. MeineDatenbankTableAdapter.Insert(Feld1, Feld2)
    5. End Sub
    6. End Class


    Damit kann sollte ich doch jetzt meine Daten in die DB schreiben können und lasse alle anderen Datenbankrelvanten Dinge wie Verbindungsaufbau usw. vom TableAdapter managen.

    Oder muss ich den Weg über das Dataset gehen? Also DatatSet befüllen und dann den TableAdapter anweisen es in die DB zu schreiben?

    Gruß

    StGo schrieb:

    MeineDatenbankTableAdapter.Insert(Feld1, Feld2)
    Echt - kompiliert das?
    Weil ich ja der Ansicht bin, zum Wesen eines Adapters gehört, dass man da nix reinschreibt.

    Und selbst wenn es ginge - wäre das klug? Weil dann sind deine Daten inne Datenbank. Aber in deinem Dataset sind sie dann noch nicht (und das wirkt sich ungünstig auf weitere Verarbeitungsschritte aus ;) ).

    Ansonsten ist Konsole einfach Crap.

    Ich denke, du wirst wesentlich schneller mit der Thematik warm, wenn du Winforms entwickelst. Weil da What you see is what you get.
    Echt - kompiliert das?

    Ich habe das soeben in einer Konsolenanwendung ein typ. Dataset getestet - das .Insert() funktioniert wirklich!
    Nur wird man mit dem Dataset in so einer Anwendung nur interne (unsichtbare) Datenmanipulationen durchführen können - zur Datenvisualisierung ist man da in der Falschen Oberfläche, denke ich...
    Ja wie schon im Post über mir das funktioniert.

    VB.NET-Quellcode

    1. Public Shared Sub LoggerInfo(ByVal message As String, ByVal Prioritaet As Integer)
    2. Try
    3. Dim fileNames As String = ""
    4. Dim lineNumber As String = ""
    5. LoggerTableAdapter.Insert(Prioritaet, DateAndTime.Now, message, fileNames, lineNumber)
    6. Catch genEx As Exception
    7. Console.WriteLine(genEx.Message)
    8. LoggerInfo(genEx.Message, 1)
    9. End Try
    10. End Sub


    Das ist ein Auszug aus meinem Logger. ich schreibe alle Schritte meiner Verarbeitung mit. Ich hatte gehofft, dass mir der TableAdapter die Verbindungsproblematik abnimmt. Da die Anwendung im Hintergrund läuft gibt es dazu keine GUI. Das Log lese ich später mit einer Extra Anwendung aus um zu sehen wo Fehler entstehen. Die Informationen sind so reichhaltig das ein Textfile zu unübersichtlich ist.

    Der erste versuch war eine Access Datenbank:

    VB.NET-Quellcode

    1. Option Explicit On
    2. Option Strict On
    3. #Region "Sonstige Importe"
    4. Imports System.Configuration
    5. Imports System.IO
    6. Imports System.Data
    7. Imports System.Data.OleDb
    8. #End Region
    9. Public Class LoggerDBClass
    10. Shared LoggerConnection As New OleDbConnection
    11. Shared LoggerCommand As New OleDbCommand
    12. 'Funktions zum Datenbakaufbau mit Statusrückgabe
    13. Public Shared Function LoggerDbConnectionOpen(ByVal Datenbankpfad As String, ByVal Open As Boolean) As Boolean
    14. If Open = True Then
    15. Dim ConnectionString As String = "Provider=Microsoft.ACE.OLEDB.12.0;" & "Data Source = " & Datenbankpfad
    16. LoggerConnection.ConnectionString = ConnectionString
    17. LoggerCommand.Connection = LoggerConnection
    18. LoggerConnection.Open()
    19. End If
    20. Dim ConnectionState As Integer = CInt(CStr(LoggerConnection.State))
    21. If ConnectionState = 1 Then
    22. LoggerDbConnectionOpen = True
    23. Else
    24. LoggerDbConnectionOpen = False
    25. End If
    26. Return LoggerDbConnectionOpen
    27. End Function
    28. 'Funktion zum Schliessen der Datenbank mit Statusrückgabe
    29. Public Shared Function LoggerDbConnectionClose(ByVal Close As Boolean) As Boolean
    30. If Close = True Then
    31. LoggerConnection.Close()
    32. End If
    33. Dim ConnectionState As Integer = CInt(CStr(LoggerConnection.State))
    34. If ConnectionState = 0 Then
    35. LoggerDbConnectionClose = True
    36. Else
    37. LoggerDbConnectionClose = False
    38. End If
    39. Return LoggerDbConnectionClose
    40. End Function
    41. 'LoggerInfo für Exceptions incl. Dateiname und Zeilennummer
    42. Public Shared Sub LoggerInfo(ByVal ex As Exception, ByVal Prioritaet As Integer)
    43. Dim trace As Diagnostics.StackTrace = New Diagnostics.StackTrace(ex, True)
    44. Dim fileNames As String = trace.GetFrame((trace.FrameCount - 1)).GetFileName()
    45. Dim lineNumber As Int32 = trace.GetFrame((trace.FrameCount - 1)).GetFileLineNumber()
    46. Try
    47. LoggerCommand.CommandText = "insert into LogFile" & "(Prioritaet, Uhrzeit, Fehlertext, Filename, Linenumber) values('" & Prioritaet & "', '" & DateAndTime.Now & "', '" & ex.Message & "', '" & fileNames & "', '" & lineNumber & "')"
    48. LoggerCommand.ExecuteNonQuery()
    49. Catch genEx As Exception
    50. Console.WriteLine(genEx.Message)
    51. End Try
    52. End Sub
    53. 'LoggerInfo für Nachrichten (kann für alles Loginformationen verwendet werden)
    54. Public Shared Sub LoggerInfo(ByVal message As String, ByVal Prioritaet As Integer)
    55. Try
    56. LoggerCommand.CommandText = "insert into LogFile" & "(Prioritaet, Uhrzeit, Fehlertext) values('" & Prioritaet & "', '" & DateAndTime.Now & "', '" & message & "')"
    57. Catch genEx As Exception
    58. LoggerInfo(genEx.Message, 3)
    59. End Try
    60. End Sub
    61. End Class


    Danach habe ich probiert eine SQL Datenbank aus VS raus zu integrieren. Damit hatte ich gehoffe das mir der TableAdapter die Datenbankverbindungsproblematik abnimmt. Ich denke so kann man gut sehen warum ich eine Konsolenanwendung nutze.

    Gruß
    Ja, so scheint mir das auch einigermaßen sinnvoll, nur zum Loggen brauchst du ja gar kein Dataset.
    Logs sind ja keine zu verarbeitenden Daten, sondern werden weggeschrieben und sind weg.
    Dafür kann man auch mal untypisiert proggen, man braucht ja nur ein Command ungefähr so zu konfigurieren:

    VB.NET-Quellcode

    1. dim con as new DBConnection(blablabla)
    2. dim cmd as DbCommand= con.CreateCommand
    3. cmd.connection=con
    4. cmd.CommandText="Insert into DolleTabelle set VielWichtiges =?"
    5. dim param=cmd.CreateParameter
    6. param.Dbtype=System.Data.DbType.String
    7. cmd.Parameters.add(param)
    Das Command kannst du nun immer wieder verwenden, und kannst viel wichtiges an cmd.Parameters(0).Value zuweisen, und dann das Command mit .ExecuteNonQuery ausführen.
    Hat deine Tabelle mehr spalten und verschiedenen Datentyps dann halt mehrere Parameter, und entsprechend konfiguriert.

    (PS: die Sql-Syntax ist glaub falsch - ich bringe die Update-Syntax immer mitte Insert-Syntax durchnander)
    Damit wäre ich ja mit meinem ersten Versuch ganz gut.

    Aber was ist mit dem Verbindungsthema? Generell so spät wie möglich öffnen und so früh wie möglich schließen. Aber... da das Log die meisten Schritte mitschreiben soll und es auch nur diese eine offene verbindung gibt würde ich sie einfach "immer" offen lassen. An diesem Punkt bin ich auf die DataSet/TableAdapter Möglichkeit gestoßen.
    Inzwischen habe ich mir dazu ein Buch bestellt weil ich das jetzt lösen will. microsoft-press.de/product.asp?cnt=product&id=ms-5467

    Ich bin mir sicher das auch dieses Thema irgendwie in der Konsolenanwendung zu machen ist.

    StGo schrieb:

    Danach habe ich probiert eine SQL Datenbank aus VS raus zu integrieren. Damit hatte ich gehoffe das mir der TableAdapter die Datenbankverbindungsproblematik abnimmt.
    Versuche folgendes in einer Forms-Anwendung:
    Da kannst du mit Daten/Neue Datenquelle hinzufügen... schön ein typ. Dataset anlegen. Da wird dir alles generiert, was du brauchst. Der Connectionstring wird automatisch in den My.Settings angelegt.
    Und da hast du dann jegliche Vorteile einer typ. Datasets beim Auswerten der Logging-Daten...
    Aber... da das Log die meisten Schritte mitschreiben soll und es auch nur diese eine offene verbindung gibt würde ich sie einfach "immer" offen lassen
    Du kannst dir aber auch ein typ. Dataset in deiner Konsolenanwendung erstellen lassen und die Daten in eine Tabelle des Datasets umlenken. Und wenn z.B. eine gewisse Anzahl Logging-Zeilen generiert wurden, kannst du die Daten dann ja gezielt in die DB rüberschicken.
    Aber zur Visualisierung deiner Daten ist die Konsolenanwendung nicht zu gebrauchen!

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „VB1963“ ()

    Klar, sehen kann man da die Daten nicht - aber zum Puffern und dann größere Portionen in die DB rüberschicken wäre ja so ein Dataset ja nicht gerade ein Unfug oder...?
    Die Verbindung ständig zur DB offen lassen und hin und wieder eine Zeile in die DB inserten, sollte man ja auch wieder nicht anstreben.
    Morgen zusammen,

    das DataSet hatte ich schon angelegt (LoggerDBDataSet.xsd). Nur leider kommt genau hier das Problem ich war der Meinung ich müßte per LoggerTableAdapter.Insert(Feld1, Feld2) die Daten einfügen. Aber wie ich hier gelernt habe schreibe ich in DataSet und sage dann dem TableAdapter das er es in die DB schreiben soll. Da binich mir noch nicht einig wie das geht.

    Ich werde mal basteln und mein Ergebnis rückmelden

    StGo schrieb:

    Da binich mir noch nicht einig...
    Ja - deine Entscheidung - hast hier ja 2 Ansätze vorliegen: Ganz ohne Dataset wäre sehr einfach, aber wie @VB1963 anmerkt, nicht die Oberkante an Performance.
    Oder halt mittm Dataset als Puffer, dass man nicht für jeden einzelnen Pups den Datenbank-Provider in Gang schmeißen muss. Dabei ist aber noch was auszudenken, wenn die Anwendung abschmiert, dass grade dann die Rest-Logs noch schnell in die DB geschrieben werden.
    Sowas kann man im Application.UnhandledException - Event einbauen.