Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
SQLite Update aktualisiert die Datenbank nicht und das Programm friert ein
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

SQLite Update aktualisiert die Datenbank nicht und das Programm friert ein

beantworten | zitieren | melden

Hallo,

ich nutze in Visual Studio 2015 SQLite 3. Meine Datenbank erstelle und bearbeite ich mit dem SQLite Manager unter Mozilla.

Leider habe ich derzeit folgendes Problem und komme echt nicht weiter:

Ich habe eine WinForm-Anwendung erstellt, die u. a. auf eine SQLite Datenbank zugreift. Das Lesen von der Datenbank funktioniert prima: ich fülle DataGridViews und kann einzelne Labels mit Werten aus der Datenbank füllen. Soweit also alles prima.

Problematisch wird es beim Schreiben in die Datenbank, bzw. beim Update. Folgendes Problem: ich möchte in einem Table einen Wert, der zuvor in einer Textbox zu Verfügung gestellt wird, in der Datenbank ablegen. Um das Ganze ein bisschen anschaulicher zu gestalten: stellt euch ein Konto vor. In die Textbox wird ein Betrag x eingetragen, der in einer Methode mit dem alten Betrag verrechnet wird (alt + neu also). Der Einfachheit halber habe ich jetzt auf sämtlichen SchnickSchnack verzichtet (z.B. Übergabe des Textboxinhalts per Variable und dergleichen) und in den CommandText meinen SQL-Befehl ausgeschrieben.

In diesem Fall soll der Wert "100" in den Table "Konten" geschrieben werden, in die Spalte "Kontoname", in die Zeile deren Eintrag "Giro_Konto" zugeordnet ist. Ich habe versucht das so zu lösen:



string myConnectionString = @"Data Source : C:\MyDataBase\Finanzplaner.sqlite; Version = 3";

SQLiteConnection myConnection = new SQLiteConnection(myConnectionString)

SQLiteCommand cmd = new SQLiteCommand();
                    cmd.CommandText = "UPDATE Konten SET Saldo = 100 WHERE Kontoname = 'Giro_Konto'";
                    cmd.Connection = myConnection;
                    myConnection.Open();

Der Code wird zwar durchlaufen, aber der Wert wird in der Datenbank nicht geupdated. Erschwerend kommt hinzu, dass keine Exception ausgelöst wird.

Ich habe dazu verschiedene Codeschnippsel ausprobiert, die alle die Datenbank updaten sollen - obiger ist sozusagen nur der letzte Entwurf^^ Aber ich bin mit meinem Latein gerade echt am Ende.

Ich habe den CommandText im SQLite Manager getestet - dort funktioniert er tadellos.

UPDATE Konten SET Saldo = 500 WHERE Kontoname = 'Giro_Konto'; 
Dort steht dann tatsächlich 500 an der entsprechenden Stelle, wo zuvor der alte Betrag stand.

Heute morgen hatte ich den Code-Block noch um


try
{
    if (myConnection.State == ConnectionState.Open)
     {
       //Messagebox ("OK ")
      }
      else
      {
      //Messagebox ("Fehlschlag")
       }
catch (SQLiteException e)
{
//MessageBox.Show(e.Message);
}
ergänzt. Per Einzelschritt konnte ich lediglich feststellen, dass der Code wie gewünscht durchlaufen wird - nur, dass eben nix in der Datenbank landet.

Jemand eine Idee, was ich (alles) falsch mache?
Gruß
Vorph
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4179

beantworten | zitieren | melden

Hallo,

führst du denn auch


/* int count = */ cmd.ExecuteNonQuery();
aus?
Der Rückgabewert gibt dir dann die Anzahl der geänderten Datensätze zurück (sollte also mindestens 1 sein).
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Hi Th69,

mein Code schaut jetzt so aus (einzige Veränderung zu oben: ich habe int i = cmd.ExecuteNonQuery eingefügt):


string myConnectionString = @"Data Source : C:\MyDataBase\Finanzplaner.sqlite; Version = 3";

SQLiteConnection myConnection = new SQLiteConnection(myConnectionString)

SQLiteCommand cmd = new SQLiteCommand();
                    cmd.CommandText = "UPDATE Konten SET Saldo = 100 WHERE Kontoname = 'Giro_Konto'";
                    cmd.Connection = myConnection;
                    myConnection.Open();

int i = cmd.ExecuteNonQuery();

MessageBox.Show(i.ToString());


Jetzt läuft der Code nicht mehr zum Ende durch - bei der Zeile


int i = cmd.ExecuteNonQuery();

schmiert das Programm ab. Ist mir ein völliges Rätsel.

Nur mal eine doofe Anfängerfrage: ist es denkbar, dass ich mir am System was zerschossen habe? Oder: Mir kommt es fast so vor, als wäre es mir aus Visual Studio raus nur möglich lesend auf die Datenbank zuzugreifen - ist sowas möglich? Irgendwelche Admin-Rechte nötig?

Alles was ich bisher mit der Datenbank gemacht habe, waren einfache Abfragen. Insert und Update funktionieren nur, wenn ich den SQLite Manager ranziehe ("SQL ausführen").
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 319

beantworten | zitieren | melden

Und was sagt die Fehlermeldung?
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Zitat
Und was sagt die Fehlermeldung?

Tja...das ist mein Problem - gar nichts: das Programm friert einfach ein und ich muss in der Taskleiste VS anklicken, um das Programm händisch abzubrechen (Stopp).

Ich habe in meinen Connection-String jetzt übrigens auch mal Read Only = false eingefügt. Ändert leider auch nix...
private Nachricht | Beiträge des Benutzers
T-Virus
myCSharp.de - Member



Dabei seit:
Beiträge: 1899
Herkunft: Nordhausen, Nörten-Hardenberg

beantworten | zitieren | melden

@GeneVorph
Dann Debugge dein Program.
Den sonst kann dir auch keiner sagen wo das Problem liegt.
Hier musst du schauen warum er hängt.
Entweder wartet er auf etwas, Timeout, oder es gibt einen anderen Grund.

Ein Program das hängt, wartet meistens auf etwas.
Aber ohne den gesamten Code kann dir dies niemand sagen.
Hier musst du deinen eigenen Code debuggen und schauen was er an dem Break Point macht.
Das gehört zu den Grundaufgaben eines Entwicklers und dies wird auch von dir erwartet.
Gründe für das hängen kann es viele geben und das erraten will heir niemand.

T-Virus
Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Hey T-Virus,

du wirst wohl leider recht haben. Ich will/wollte aber auch niemanden zum Raten anhalten - ich hatte die leise Hoffnung, bei den Pros würden die Glocken läuten, weil ich vielleicht einen ganz offensichtlichen Fehler gemacht hätte. Dem scheint leider nicht so zu sein.

Dennoch Danke für eure Hilfe.
private Nachricht | Beiträge des Benutzers
Coffeebean
myCSharp.de - Team

Avatar #avatar-3295.gif


Dabei seit:
Beiträge: 2461
Herkunft: Deutschland/Schweiz

beantworten | zitieren | melden

Hallo GeneVorph,

fang mal alle Exceptions ab, dann hast du sicher auch eine Fehlermeldung.

Verwende ausserdem usings. Mit dem Debugger und ner Fehlermeldung siehst du sicher, was da vor sich geht. Auch SQL-Parameter sind sicher nicht schlecht anzuschauen.

Weiter gibt es unzählige Beispiele im Netz zu sqlite updates. Hast du dich da an einem orientiert?

Gruss

Coffeebean
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Hey Coffeebean,

vielen Dank für die Tipps - ich bin Hobby-Coder und habe gerade erst die ersten Gehversuche hinter mich gebracht. Deine Hinweise geben mir jetzt mal eine Richtung, wie ich die Fehlersuche sinnvoll angehen kann, das hilft schon eine Menge, sonst wird es ein kopfloses rumprobieren und zusammenklauben.
Zitat
Weiter gibt es unzählige Beispiele im Netz zu sqlite updates. Hast du dich da an einem orientiert?

Hier muss ich sagen: leider ja. An mehreren^^ Die SQLite-Statements sind nicht umsonst deklarativ, da gibt's also nicht so viele Fragezeichen. Leider dann aber beim Arbeiten mit denselben in C#. Was viele "Erklärer" leider nicht machen, ist a) ihren Stoff didaktisch sinnvoll strukturieren und b) die Funktionsweisen erläutern. Bei mir ist das z. B. ExecuteNonQuery. Die reine Formulierung würde mich darauf schließen lassen, dass ich bei allem, was keine reine Datenbankabrage ist (Lesen) diese Methode nicht benötige. Seltsamerweise taucht sie dann doch in gut der Hälfte aller Fälle auf, wenn es um INSERT und UPDATE geht.

Konkret orientiert habe ich mich hieran, daran, und dort.
private Nachricht | Beiträge des Benutzers
unconnected
myCSharp.de - Member

Avatar #avatar-3200.jpg


Dabei seit:
Beiträge: 862
Herkunft: Oerlinghausen/NRW

beantworten | zitieren | melden

Nur die hälfte? Das würde mich wundern.

Inserts und Updates sind "NonQuery´s" da sie keine Ergebnismengen zurück geben, ausser die Anzahl der geänderten Datensätze.

Du musst deinen Code Debuggen dann bekommst Du auch eine Fehlermeldung (Exception Settings: Common Runtime Exceptions -> haken setzen!). ALternativ kannst Du auch ein try & catch mit einer COnsolenausgabe machen.
private Nachricht | Beiträge des Benutzers
Coffeebean
myCSharp.de - Team

Avatar #avatar-3295.gif


Dabei seit:
Beiträge: 2461
Herkunft: Deutschland/Schweiz

beantworten | zitieren | melden

Blöde Frage: Du hast die Db schon vorher erstellt, oder?

Schnell ausprobiert, keine Garantie auf Richtigkeit

static void Main(string[] args)
        {
            try
            {
                new Program().Run();
            }
            catch (Exception exception)
            {
                //...
            }

            Console.ReadLine();
        }

        public void Run()
        {
             string databaseName = "MyTestDatabase.sqlite";
            string baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            string databasePath = Path.Combine(baseDirectory, databaseName);
            SQLiteConnection.CreateFile(databasePath);

            Console.WriteLine("File created at {0}", databasePath);

            string connectionString = $"Data Source={databaseName};Version=3;";

            using (SQLiteConnection connection = new SQLiteConnection(connectionString))
            {
                connection.Open();
                string createSqlStatement = "CREATE TABLE Konten (KontoName VARCHAR(20), Saldo INT)";
                ExecuteNonQuery(createSqlStatement, connection);

                string insertSqlStatement = "INSERT INTO Konten (KontoName, Saldo) values ('Girokonto', 1000)";
                ExecuteNonQuery(insertSqlStatement, connection);

                ReadAllValues(connection);

                string updateSqlStatement = "UPDATE Konten SET Saldo = 2000 WHERE KontoName = 'Girokonto'";
                ExecuteNonQuery(updateSqlStatement, connection);

                ReadAllValues(connection);
            }
        }

        public int ExecuteNonQuery(string sqlText, SQLiteConnection connection)
        {
            using (SQLiteCommand command = new SQLiteCommand(sqlText, connection))
            {
                return command.ExecuteNonQuery();
            }
        }

        public void ReadAllValues(SQLiteConnection connection)
        {
            string sql = "SELECT * from Konten";

            using (SQLiteCommand command = new SQLiteCommand(sql, connection))
            {
                SQLiteDataReader reader = command.ExecuteReader();
                while (reader.Read())
                {
                    Console.WriteLine("Name: " + reader["KontoName"] + "Saldo: " + reader["Saldo"]);
                }
            }
        }

Funktioniert bei mir einwandfrei...
File created at c:\...\bin\Debug\MyTestDatabase.sqlite
Name: GirokontoSaldo: 1000
Name: GirokontoSaldo: 2000

Gruss

Coffeebean
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4179

beantworten | zitieren | melden

Und falls du noch nicht mit dem Debugger gearbeitet hast: [Artikel] Debugger: Wie verwende ich den von Visual Studio?
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Zitat
Blöde Frage: Du hast die Db schon vorher erstellt, oder?
Ja, habe ich ;-)

ich konnte deinen Code jetzt noch nicht testen - bin gerade erst heim gekommen. ABER: ich habe mal meinen Code, hm, "zurückgebaut" und festgestellt, dass u. a. folgender Codeblock Probleme macht, und zwar:

public object LoadDataGridView(string tableName)
        {
            try
            {
                //Connection erstellen --> der connectString gibt dabei den Pfad an.
                SQLiteConnection myConnection = new SQLiteConnection(connectStringPath);

                //Verbindung zur Datenbank öffnen
                myConnection.Open();

                //Einen Befehls-String erstellen, der den gewünschten SQLite-Befehl enthält (i.d.F.: SELECT *)
                string myCommandString = "SELECT * FROM @tableName";

                //Die Variable @tableName mittels SQLite-Befehl an einen SQLite-Parameter übergeben
                SQLiteCommand myCommand = new SQLiteCommand(myCommandString, myConnection);
                myCommand.Parameters.AddWithValue("@tableName", tableName);

                myCommand.ExecuteNonQuery();
               
                //DataAdapter erzeugen, der die Daten für das DataGridView "adaptiert" und einen DataTable der die Adapterdaten entgegennimmt
                SQLiteDataAdapter myAdapter = new SQLiteDataAdapter(myCommandString, myConnection);
                DataTable myTable = new DataTable();

                //Table füllen, Verbindung schließen und das DataTAble-Object mit Hilfe der object-Variable an die aufrufende Methode zurückgeben
                myAdapter.Fill(myTable);
                myConnection.Close();

                return myTable;
            }   
            catch(SQLiteException e)
            {
                MessageBox.Show(e.ToString());
                return null;
            }                  
        }
Das ist Code, der obigem Code zuspielt - die Problemstelle ist auch vollkommen klar, sie liegt hier:

myCommand.Parameters.AddWithValue("@tableName", tableName);

                myCommand.ExecuteNonQuery();
               

Wenn ich nämlich diese Zeilen auskommentiere und statt dem @tableName das Ganze so mache,

string myCommandString = "SELECT * FROM '" + tableName + "';

dann funktioniert es. Ich wollte mit @tableName eigenlich SQL-Injections vorbeugen. Ich hatte ähnlichen Code schon mal im Projekt verwendet, und da funktioniert es so:


SQLiteConnection myConnection = new SQLiteConnection(myConnectionString);

//Verbindung öffnen
myConnection.Open();

//String mit Variablen
string comString = "INSERT INTO Konten (id, Name, Nummer) VALUES (@id, @Name, @Nummer);

SQLiteCommand myCommand = new SQLiteCommand(comString, myConnection);

//Die Variable @tableName mittels SQLite-Befehl an einen SQLite-Parameter übergeben
myCommand.Parameters.AddWithValue("@id", id);
myCommand.Parameters.AddWithValue("@Name", Name);
myCommand.Parameters.AddWithValue("@Nummer", Nummer);

//Ausführen
myCommand.ExecuteNonQuery();

myConnection.Close();

Wie gesagt, die Chose funktioniert. ABER: ich vermute mal stark, dass ein konzeptionelles Missverständnis vorliegt - womöglich seht ihr das an meinen Kommentaren. Nach meinem Verständnis hätte es funktionieren müssen; ich weiß jetzt wie es funktioniert (s. CodeBlocks 3+4) bin aber unglücklich von dem @ abrücken zu müssen, weil man allenthalben liest, dass man so effizient Injections verhindert :( Sorry, wenn das sehr noobish rüwwerkommt, aber man kann sich noch so viel einlesen: vor Fehlannahmen ist man nicht gefeit. Vielleicht könnt ihr mir aber sagen, wie ich es hätte machen sollen damit es funktioniert. Meine Lösung kann nicht der einzige (richtige?) Wes sein, oder?

Danke für eure Inputs - ich werde alles sorgfältig prüfen, bzw. bin mitten im Debugging!
private Nachricht | Beiträge des Benutzers
bb1898
myCSharp.de - Member



Dabei seit:
Beiträge: 110

beantworten | zitieren | melden

Da liegt wirklich ein Missverständnis vor: Tabellen- und Feldnamen einerseits, Feldwerte andererseits sind zwei Paar Stiefel und können nicht gleich behandelt werden. Tabellen- und Feldnamen sind im Normalfall ja Konstanten und können einfach in den SQL-Text geschrieben werden. Sind es doch Variable, hilft nur erst ordentlich überprüfen (falls vom Benutzer gekommen) und dann in den Text einbauen. Bei letzterem würde ich eher zu string.format("xxx {0}", variable) tendieren, aber das geht jetzt in die Geschmacksfragen.

Mit SQL-Parametern kann und soll man Feldwerte übergeben, sonst nichts. In Deinem funktionierenden Beispiel wird ja auch genau so verfahren.

Moderationshinweis von Abt (12.07.2016 - 21:49:33):

Nochmals: bitte keine Full Quotes!
[Hinweis] Wie poste ich richtig?

private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

beantworten | zitieren | melden

Danke für deine Antwort. OK, ich will jetzt nicht die Geschmacksfrage stellen, muss dennoch kurz darauf zu sprechen kommen:
Zitat von bb1898
Zitat von GeneVorph
In Deinem funktionierenden Beispiel wird ja auch genau so verfahren.
das vielleicht schon, aber die ursprüngliche Absicht mich vor SQL-Injections abzusichern - die bleibt bei diesem Vorgehen ja außen vor. Wie würde denn ein kurzer Schnippsel Code aussehen, der das berücksichtigt?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 16145

beantworten | zitieren | melden

SqlParameter sind - wie der Name sagt - für die Parameter, also die Werte da; nicht für Bezeichnungen des Schemas, wie Spalten- oder Tabellennamen.
Bei Table Names sollte empfiehlt es sich diese über Whitelists verfügbar zu machen; nicht vollständig variabel!

Alternativ könntest Du es über den SqlCommandBuilder und QuoteIdentifier machen.

string safeTableName = sqlCmdBuilder.QuoteIdentifier(tableName);
var query = $"SELECT * FROM {safeTableName}"

QuoteIdentifier ist für Felder und den Tabellennamen da, sodass diese Werte dynamisch sicher sind.
- performance is a feature -

Microsoft MVP - @Website - @blog - @AzureStuttgart - github.com/BenjaminAbt
private Nachricht | Beiträge des Benutzers
Coffeebean
myCSharp.de - Team

Avatar #avatar-3295.gif


Dabei seit:
Beiträge: 2461
Herkunft: Deutschland/Schweiz

beantworten | zitieren | melden

Hallo GeneVorph,
Zitat
Wie würde denn ein kurzer Schnippsel Code aussehen, der das berücksichtigt?

meinst du sowas:

command.Parameters.AddWithValue("@Saldo", 1000);
?

Gruss

Coffeebean
private Nachricht | Beiträge des Benutzers
ErfinderDesRades
myCSharp.de - Experte

Avatar #avatar-3151.jpg


Dabei seit:
Beiträge: 5409

beantworten | zitieren | melden

Zitat von GeneVorph
... ich bin Hobby-Coder und habe gerade erst die ersten Gehversuche hinter mich gebracht....
Dann würde ich erstmal ganz pauschal von Anwendungen mit Datenbank-Zugriffen abraten.

Dahinter stecken jede Menge Grundlagen.
Hinzu kommen jede Menge Tücken.
Und überhaupt muss man dafür die abstrakten Konzepte eines relationalen Datenbank-Modells verstehen und befolgen.
Hinzu kommen Pattern, die ebenfalls zu befolgen sind - etwa dass man im Client ebenfalls ein Datenmodell unterhalten sollte, also wer ein DatagridView aus einer Datenbank befüllt steht schon mit beiden Beinen inne Hölle, denn man sollte immer sein Datenmodell befüllen, keine Controls!

Bei Interesse an Tutorials from Scratch sag bescheid - sind allerdings am Beispiel vb erläutert.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von ErfinderDesRades am .
Der frühe Apfel fängt den Wurm.
private Nachricht | Beiträge des Benutzers
GeneVorph
myCSharp.de - Member



Dabei seit:
Beiträge: 166

Themenstarter:

[gelÖst]

beantworten | zitieren | melden

Kurze Rückmeldung: ich konnte das Problem lösen! Vielen Dank an alle Helfer!

Das Problem war - wie hier schon richtig vermutet - das Warten auf ein Ereignis, das schlicht nicht eintreten konnte. Zwei Fehler gingen dem Voraus: meine Unerfahrenheit (vergessen eine Connection zu schließen) und dazwischen eine Methode, die auf einen Zugriff auf die Datenbank wartete, der aber so nicht eintreten konnte.

Ich habe zwar jetzt viel Zeit ins Debugging investiert, aber es hat sich sehr gelohnt. Aus Fehlern lernt man tatsächlich :)

Dankeschön @ all!
private Nachricht | Beiträge des Benutzers
Entwickelt mit ♥ und ASP.NET Core. Version 0.1.334+189bf68133 vom Uhr. Betrieben auf Azure App Service (debian.10-x64 mit .NET 5.0.10)
Copyright 2003-2021 myCSharp.de - Alle Rechte vorbehalten. Benutzerinhalte unterliegen cc-by-sa 4.0