Verwendetes Datenbanksystem: SQL
Hallo Zusammen,
ich möchte mich gleich vorab entschuldigen. Was das Programmieren angeht, bin ich noch sehr grün hinter den Ohren.
Ich habe jetzt auch schon so einiges versucht und auch gegoogelt, aber ich komme leider nicht weiter. Vielleicht habt Ihr ja eine Idee.
In SQL habe ich eine Datenbank programmiert, die Kunden- und Fahrzeugdaten verwaltet und es ermöglicht Verträge anzulegen oder auch Fahrzeuge zu reservieren. Dafür werden verschiedene Primärschlüssel und Fremdschlüssel in den jeweiligen Tabellen hinterlegt.
Funktionieren tut soweit alles, außer das Speichern der Vertragsdaten im Formular Vertrageinzel. Hier kommt es beim speichern zu folgenden Fehler:
************** Ausnahmetext **************
System.IndexOutOfRangeException: An der Position 0 befindet sich keine Zeile.
bei System.Data.RBTree`1.GetNodeByIndex(Int32 userIndex)
bei System.Data.DataRowCollection.get_Item(Int32 index)
bei Autovermietung2030.Vertrageinzel.PreisBerechnen() in C:\Users\marco\Desktop\Developer\CSHP19E - Datenbanken Fortgeschritten\Programme\Autovermietung2030\Vertrageinzel.cs:Zeile 20.
bei Autovermietung2030.Vertrageinzel.fNummerComboBox_SelectedIndexChanged(Object sender, EventArgs e) in C:\Users\marco\Desktop\Developer\CSHP19E - Datenbanken Fortgeschritten\Programme\Autovermietung2030\Vertrageinzel.cs:Zeile 95
Ich habe aus Übersichtszwecken nur den ersten Teil der Fehlermeldung angehangen. Beim Rest handelt es sich vermutlich um Folgefehler.
Hier mal die betreffenden Codeblöcke:
public partial class Vertrageinzel : Form
{
public void PreisBerechnen()
{
float preisProTag;
int anzahlTage;
auto2030DataSet.FahrzeugDataTable tabelle = this.fahrzeugTableAdapter.GetPreisProTag(Convert.ToInt32(fNummerComboBox.SelectedValue));
preisProTag = Convert.ToSingle(tabelle.Rows[0]["preisProTag"]);
anzahlTage = (rueckgabeDatumDateTimePicker.Value - ausleihDatumDateTimePicker.Value).Days;
anzahlTage = anzahlTage + 1;
Console.WriteLine(preisProTag);
gesamtpreisTextBox.Text = (anzahlTage * preisProTag).ToString();
}
public Vertrageinzel()
{
InitializeComponent();
}
private void Vertrageinzel_Load(object sender, EventArgs e)
{
this.fahrzeugTableAdapter.Fill(this.auto2030DataSet.Fahrzeug);
this.kundeTableAdapter.Fill(this.auto2030DataSet.Kunde);
this.vertragTableAdapter.Fill(this.auto2030DataSet.Vertrag);
if (fNummerComboBox.Items.Count == 0 || kNummerComboBox.Items.Count == 0)
{
MessageBox.Show("Es gibt keine Fahrzeuge oder Kunden für einen Vertrag!", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Close();
}
else
{
vertragBindingSource.AddNew();
kNummerComboBox.SelectedIndex = 0;
fNummerComboBox.SelectedIndex = 0;
ausleihDatumDateTimePicker.Value = DateTime.Now;
rueckgabeDatumDateTimePicker.Value = DateTime.Now.AddDays(1);
}
}
private void buttonSpeichern_Click(object sender, EventArgs e)
{
this.Validate();
this.vertragBindingSource.EndEdit();
this.tableAdapterManager.UpdateAll(this.auto2030DataSet);
this.fahrzeugTableAdapter.AusgeliehenSetzen(Convert.ToInt32(fNummerComboBox.SelectedValue));
Close();
Bei laden des Formulars wird ja eine neue Instanz angelegt, mir ist unklar, warum dann keine Zeile beim speichern gefunden werden kann. Mein Fachlehrer (Fernlehrgang) meint es handelt sich vermutlich um ein Timingproblem und ich solle die Datenbank einmal löschen und neu schreiben. Das halte ich zwar nicht für eine gute Praxis, getan habe ich es aber trotzdem. Das Problem bleibt aber bestehen.
Hat jemand von euch erfahreneren SQL-Programmieren eine Idee? Es muss ja nich gleich die Problemlösung sein, aber vielleicht habt Ihr Ansätze für die Fehlersuche?
Im Anhang das ganze Projekt, falls es von Interesse ist.
Vielen Dank für Eure Hilfe,
Gruß Marco
Leider ist im gezeigten Code nicht ersichtlich, wo genau 'PreisBerechnen' aufgerufen wird und was genau 'GetPreisProTag' macht. Ohne die Information wird Dir niemand helfen können.
Ansonsten: Nutze den Debugger um Schritt für Schritt den Code durchzugehen. Dann wirst Du schon sehen, dass Du 'PreisBerechnen' offenbar aufrufst, bevor die Tabelle gefüllt wird. Kleiner Hinweis: fNummerComboBox_SelectedIndexChanged ist der Punkt, an dem Du das Debuggen beginnen solltest...
Vielen Dank für die schnelle Antwort wcseller.
'PreisBerechnen' wird aufgerufen bei:
private void ausleihDatumDateTimePicker_ValueChanged(object sender, EventArgs e)
{
PreisBerechnen();
}
private void rueckgabeDatumDateTimePicker_ValueChanged(object sender, EventArgs e)
{
PreisBerechnen();
}
private void fNummerComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
PreisBerechnen();
}
}
'GetPreisProTag' habei ich als Methode im FahrzeugTabelAdapter hinterlegt. Die Methode soll den Preis für das aktuell gewählte Fahrzeug beschaffen.
SELECT fNummer, bezeichnung, kennzeichen, preisProTag, typ, ausgeliehen
FROM Fahrzeug
WHERE (fNummer = @fNummer)
Als Methode wird dann ein Datatable zurückgegeben.
Die Tabelle sollte eigentlich schon beim Aufruf des Formulars mit den Indezes 0 für die fNummerComboBbx und kNummerComboBox gefüllt sein. Also auch wenn nichts ausgewählt wird sollten Werte der Tabelle stehen. Das wird beim Loadvorgang des Formulares erledigt:
private void Vertrageinzel_Load(object sender, EventArgs e)
{
this.fahrzeugTableAdapter.Fill(this.auto2030DataSet.Fahrzeug);
this.kundeTableAdapter.Fill(this.auto2030DataSet.Kunde);
this.vertragTableAdapter.Fill(this.auto2030DataSet.Vertrag);
if (fNummerComboBox.Items.Count == 0 || kNummerComboBox.Items.Count == 0)
{
MessageBox.Show("Es gibt keine Fahrzeuge oder Kunden für einen Vertrag!", "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
Close();
}
else
{
vertragBindingSource.AddNew();
kNummerComboBox.SelectedIndex = 0;
fNummerComboBox.SelectedIndex = 0;
ausleihDatumDateTimePicker.Value = DateTime.Now;
rueckgabeDatumDateTimePicker.Value = DateTime.Now.AddDays(1);
}
}
Danke für die Hilfe und den Input.
Gruß Marco
Hallo,
Also auch wenn nichts ausgewählt wird sollten Werte der Tabelle stehen.
Darauf sollte man sich beim Programmieren nicht verlassen, sondern möglichst solche Fälle abfangen.
Setze einen Haltepunkt auf die Zeile preisProTag = ...
in PreisBerechnen()
und schau dir im Debugger die Variablen tabelle
sowie fNummerComboBox.SelectedValue
an. Wenn du noch nicht (intensiv) mit dem Debugger gearbeitet hast, dann s. [Artikel] Debugger: Wie verwende ich den von Visual Studio?
Die Tabelle sollte eigentlich... gefüllt sein.
Debugger gibt es, weil so vieles bei der Programmierung nicht das ist oder tut was es soll 😉
Die Meldung sagt "An der Position 0 befindet sich keine Zeile." mit der Angabe "bei Autovermietung2030.Vertrageinzel.PreisBerechnen()".
Die einzige Stelle in dieser Methode wo auf Position 0 zugegriffen wird, ist das mit tabelle.Rows[0]. Es gibt also scheinbar wirklich keine Row[0].
es handelt sich vermutlich um ein Timingproblem und ich solle die Datenbank einmal löschen und neu schreiben.
Jemand vom "Fach" rät das? Klingt gruslig. Was könnte ein Timingproblem sein? Datenbank neu schreiben, statt nachzusehen was das Programm tut?
Da es mehr oder minder offensichtlich ist, dass wir hier keinen Timingfehler haben, sondern dass Datensätze einfach zur "gedachten Zeit" nicht existieren und die Prinzipien von defensiver Programmierung nicht angewendet werden, hab ich auch den Thementitel angepasst.
Vielen Dank für die Hilfe an Euch.
Im Debugger-Modus kann ich das Formular gar nicht erst öffnen, dort bekomme ich die Exception Row 0 nicht vorhanden direkt. Starte ich das Programm ohne Debuggen (also Strg F5) dann komme ich in das Formular und der Preis pro Tag wird korrekt berechnet. Die Methode funktioniert also auf einmal und Spalte 0 ist somit auch vorhanden. Nur beim speichern oder selbst bei Abbrechen bekomme ich dann die Excpetion. Die Row ist dann auch tatsächlich nicht vorhanden. Aber wie kann dann die
Ich sehe leider keinen Zusammenhang zwischen dem ausführen bzw. nicht ausführen des Debuggers und dem Verhalten Row vorhanden bzw. nicht vorhanden. Wie kann es denn dann aber sein, das die 'GesamtPreisTextBox' den korrekten Preis anzeigt?
Gruß Marco
Zitat von Tuskpack
Vielen Dank für die Hilfe an Euch.
Im Debugger-Modus kann ich das Formular gar nicht erst öffnen, dort bekomme ich die Exception Row 0 nicht vorhanden direkt.
Dann hast Du den Breakpoint vergessen. Siehe Anleitung zum Debugger.
Ich sehe leider keinen Zusammenhang zwischen dem ausführen bzw. nicht ausführen des Debuggers und dem Verhalten Row vorhanden bzw. nicht vorhanden.
Sofern Du nichts extra eingebaut hast, und die Assemblies die gleichen sind, kanns am Debugging nicht hängen.
Dann machst irgendwas falsch.
Wie kann es denn dann aber sein, das die 'GesamtPreisTextBox' den korrekten Preis anzeigt?
Spricht auch dafür, dass Du hier was nicht ganz richtig bedienst.
Wenn keine Row existiert, dann stürzt die Anwendung bei
preisProTag = Convert.ToSingle(tabelle.Rows[0]["preisProTag"]);
ab und kommt gar nicht zur genannten GesamtPreisTextBox-Zuweisung.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Wenn Du, wie von mir empfohlen einen BreakPoint in fNummerComboBox_SelectedIndexChanged und einen in Vertrageinzel_Load setzt, wirst Du bemerken, dass fNummerComboBox_SelectedIndexChanged VOR Vertrageinzel_Load ausgeführt wird. Das heisst, Deine fNummerComboBox löst das Ereignis SelectedIndexChanged bereits aus, bevor dein Form das OnLoad-Ereignis auslöst und damit Vertrageinzel_Load ausführt.
Das liegt daran, dass Du höchstwahrscheinlich bereits im Designer für fNummerComboBox einen Wert setzt. Das setzen dieses Wertes wiederum führt dazu, dass fNummerComboBox_SelectedIndexChanged aufgerufen wird.
Der Ablauf ist also so:
Du siehts also, dass Du das SelectedIndexChanged-Ereignis bis NACH der Ausführung von Vertrageinzel_Load ignorieren musst.
Hier findest Du noch ein paar Informationen zur Reihenfolge der ausgelösten Events:
Reihenfolge der Ereignisse - Windows Forms .NET Framework | Microsoft Learn