PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C#] String in Decimal casten


()V()r.Freeze
2010-08-15, 22:12:58
Hi all,
ich habe folgendes Problem: Ich versuche aus einem in eine Textbox eingegebenen String eine Zahl vom Datentyp Decimal zu machen, um diesen Wert im Attribut eines Objektes zu speichern. Das Objekt wird dann in die Datenbank geschrieben (SQL Server 2008). Ich gehe folgendermaßen vor:

// Schema zur Konvertierung
CultureInfo culture = new CultureInfo("de-DE", false);

// Einlesen und casten der Zahl
Objekttyp.zahl = Convert.ToDecimal(textBoxBlaBlubb.Text, culture);

Problem: Egal ob ich . oder , als Trennzeichen verwende, in der Datenbank steht letztendlich bei 100,45/100.45 ein Wert von 10045. Weiß jemand Abhilfe?

Kurz: In die Textbox soll ein Betrag eingegeben werden, z.B. die 100,45. Daraus soll ein Decimal gemacht werden (Datentyp des Zielattributs in der Datenbank). Entsprechend soll das ganze auch rückwärts gehen: Aus einem Decimal in der Datenbank soll ein korrekt angezeigter Betrag in der Textbox werden.

Mir gehts wie immer nicht um vorgekauten Code (ist natürlich auch gern gesehen). Bin für jeden Tipp und jede Anregung dankbar.

Monger
2010-08-15, 22:36:04
Sieht eigentlich alles richtig aus. Hast du mal reindebuggt, wie deine Decimal Zahl nach der Formatierung aussieht? Ich vermute eher, dass das irgendwas auf Datenbank Seite ist.

Von was für einem Typ ist denn Objekttyp.zahl? Wenn du sagst "Casten" - auf was castest du denn? Weil eigentlich kommt in deinem Beispiel kein Casten vor.

()V()r.Freeze
2010-08-15, 22:45:26
Sorry, vielleicht der falsche Begriff. Also mit Casten mein ich das umwandeln von String in Decimal bzw. umgekehrt.
Was du sagst trifft in der Tat zu: Die Zahl kommt im System richtig an, das hab ich gerade mit nem Label kontrolliert. Beim schreiben in die DB wird das Komma/Punkt (beides getestet) "verschluckt".

Objekt.Zahl ist vom Datentyp Decimal (Ist ein Entity-Object, als EDMX vorliegend)

Berni
2010-08-15, 22:47:49
Dann wird wohl ein Fehler beim Übertragen in die Datenbank gemacht. Welche Datenbank nutzt du und wie ist der Code dafür? Bei PreparedStatements sollte eigtl. der Treiber das richtige Übertragungsformat sicherstellen aber möglicherweise muss hier auch die Locale richtig eingestellt werden.

()V()r.Freeze
2010-08-15, 22:50:58
Aaaaalso :D
- Datenbank ist ein SQL Express-Server (2008er)
- Im Datenbank-Schema ist eine Tabelle xy mit einer Spalte vom Datentyp DECIMAL
- Aus der Datenbank wurde in Visual Studio ein EDMX-Modell generiert
- Mit dem Entity Framework wird mit diesem Modell gearbeitet (über einen Kontext)
- Änderungen am Objekt werden über den Kontext gespeichert (Objektzuweisung und dann SaveChanges())

()V()r.Freeze
2010-08-15, 23:02:42
Kommando zurück. Wenn in der Datenbank statt Decimal Numeric benutzt wird funtz alles - Ka warum, aber nu bleibts so. Danke euch beiden!

Matrix316
2010-08-16, 10:44:58
Hm, schonmal ohne das Culture Dingens probiert?

Monger
2010-08-16, 11:16:07
Hm, schonmal ohne das Culture Dingens probiert?
CultureInfo implementiert IFormatProvider, ist also schon richtig. Und du musst nunmal die Kultur-Einstellungen kennen, um unterscheiden zu können ob jetzt das Dezimaltrennzeichen ein Punkt oder ein Komma ist.

Ist natürlich brandgefährlich. Wegen solchen Fehlern hat die NASA schon einige Satelliten verschossen! ;)

Matrix316
2010-08-16, 12:10:00
Das mein ich. Was wenn durch das Culture Format die Zahl so geändert wird, dass es die Datenbank nicht richtig abspeichert? Das ist garnet so trivial. ;)

Monger
2010-08-16, 12:41:27
Sobald du die Zahl in der Hand hast, ist die kulturneutral. Die Darstellung einer Zahl kann kulturabhängig sein (die Zahl "4" kann man schreiben als "IV", oder auch "IIII", oder "0100" etc.), die Zahl selbst nicht.

Sobald du die Zahl korrekt in Decimal geparst hast, ist sie einfach nur noch eine Zahl. Und der Schritt hat ja funktioniert.

Das Problem hier ist offenbar, dass die Datenformate in der Datenbank nicht 1:1 dem entsprechen was in C# darunter verstanden wird. Was hier ein Decimal ist, ist dort ein Numeric.

Matrix316
2010-08-16, 12:51:28
Also ich hab auch schon Zahlen in Decimal Datenbankfelder reingepackt und da ist eigentlich immer der Dezimaltrenner erhalten geblieben. Aufpassen muss man bei englisch-deutsch, weil einmal punkt und einmal Komma bzw. zwischen Visual Studio und SQL Server. Da gibts irgendwo unterschiede.

stav0815
2010-08-16, 13:20:14
Eventuell liegt hier auch eine Limitierung/Missmatch in der ConvertTo-Methode vor.

Gibt es denn unterschiede in der Methodik von Convert und Parse?
Und wie wäre es mit

objekttyp.zahl = TextBoxBlaBlubb.Text.ToDecimal();

?

Matrix316
2010-08-16, 20:08:45
Wo gibts denn ToDecimal? Jedenfalls net unter asp.net. ;)

Btw. waren denn beim Decimal Feld der Tabelle auch die Nachkommastellen eingegeben? Weil Decimal(18,0) macht aus 1,6 2 und aus 1.6 16 - und Convert.toDecimal macht aus 1,6 eben 1.6 und so könnte es sein, dass deswegen das Trennzeichen weggemacht wurde. :)

PatkIllA
2010-08-16, 20:20:41
Eigentlich sollte es bei der Umwandlung von Zahlen in Strings und umgekehrt mindestens eine Warnung geben, wenn man das ohne IFormatProvider macht.

new CultureInfo("de-DE", false);Selbst das ist schon gefährlich, denn de-DE ist das was der Benutzer eingestellt hat.
Entweder CultureInfo.CurrentCulture oder CultureInfo.InvariantCulture

RattuS
2010-08-16, 20:57:17
Viel schlimmer ist, dass du davon ausgehst, dass die TextBox immer eine gültige Dezimalzahl beinhaltet. Casting und Parsing sind zwei unterschiedliche Dinge. Dein Beispiel ist ein riskanter Cast. Sicher wird es erst, wenn du die Eingabe versuchst zu parsen und ungültige Eingaben blockierst.


decimal myValue;
if (decimal.TryParse(textBox.Text, out myValue))
{
Objekttyp.zahl = myValue;
}


Übrigens ist CultureInfo.CurrentCulture der Standardparameter, falls den o.g. Methoden nicht übergeben. Die Microsoft-Richtlinie erwartet die Übergabe zwar immer, warnt in VS aber nur bei aktivierter Code-Sicherheit.

Monger
2010-08-16, 22:14:56
Viel schlimmer ist, dass du davon ausgehst, dass die TextBox immer eine gültige Dezimalzahl beinhaltet. Casting und Parsing sind zwei unterschiedliche Dinge. Dein Beispiel ist ein riskanter Cast.
Nein, ist es nicht. Das ist kein Cast. Convert.ToDecimal(String, CultureInfo) macht auch nichts anderes als mit der entsprechenden Culture zu parsen. Wenn man die CultureInfo weglässt, halt mit der Current Culture. Wenn das nicht geht, fliegt da auch ne Exception.

Wo gibts denn ToDecimal? Jedenfalls net unter asp.net. ;)

Convert.ToDecimal. Und das ist im Standard .NET Framework drin, ist also grundsätzlich mal von allen .NET Sprachen aus aufrufbar - also auch ASP.NET

RattuS
2010-08-16, 23:58:43
Nein, ist es nicht. Das ist kein Cast. Convert.ToDecimal(String, CultureInfo) macht auch nichts anderes als mit der entsprechenden Culture zu parsen.
Stimmt, denn ein Cast wäre (decimal)textBox.Text, was aber sowieso nicht funktioniert, weil es nicht akzeptiert wird. Der entscheidende Unterschied ist, dass die von dir genannte Parse-Methode überhaupt eine Exception wirft, was Mist ist, wenn man genauso gut eine Methode wählen kann, die das Exception Handling von vornherein beinhaltet.

Monger
2010-08-17, 09:31:05
Der entscheidende Unterschied ist, dass die von dir genannte Parse-Methode überhaupt eine Exception wirft, was Mist ist, wenn man genauso gut eine Methode wählen kann, die das Exception Handling von vornherein beinhaltet.
Jetzt wirds philosophisch...

Exception Handling ist ein bißchen mehr, als nur Exceptions zu fangen. TryParse enthält kein Exception Handling, sondern unterdrückt es.

Natürlich gibt es Fälle wo ein TryParse Sinn macht (vorallem in Performance-kritischen Situationen), aber das Beispiel was du geschrieben hast, ignoriert ja einfach nur die Exception, und behandelt sie nicht. Das ist in meinen Augen noch schlechter, als die Exception durchlaufen zu lassen.

PatkIllA
2010-08-17, 10:11:15
Die gültige Eigabe kann man ja auch schon mit Validierung beim Control überprüfen und dann ist eine Exception in der darunter liegenden Logik schon das richtige.

dann darf allerdings nicht sowas kommen


try
{
...
}
catch
{
}
finally
{
MessageBox.Show("Erfolgsmeldung");
}

Das Beispiel habe ich schon in echt gesehen. try, catch mach gar nichts so schon öfters.

RattuS
2010-08-17, 16:07:59
...aber das Beispiel was du geschrieben hast, ignoriert ja einfach nur die Exception, und behandelt sie nicht. Das ist in meinen Augen noch schlechter, als die Exception durchlaufen zu lassen.
Ich verstehe deinen Ansatz nicht. Warum sollte man überhaupt auf eine Exception, die nicht umgebungsabhängig geworfen wird, eingehen, wenn man sie von vornherein ausschließen kann?

Validierung in der Präsentation ist ja schön und gut, aber keine ausreichend zuverlässige Programmierung in meinen Augen, denn gerade bei Team-Projekten werden so manche Methoden am Ende doch unzweckmäßig verwendet und dann fehlt die Validierung. Reine Erfahrung.

Monger
2010-08-18, 01:15:42
Ich verstehe deinen Ansatz nicht. Warum sollte man überhaupt auf eine Exception, die nicht umgebungsabhängig geworfen wird, eingehen, wenn man sie von vornherein ausschließen kann?

Erstens: dein "If...Then" alleine reicht ja nicht. Du musst auch noch irgendwie angemessen auf diesen Fehler reagieren. Was soll passieren? In eine Log Datei schreiben? Eine Message Box werfen? Was genau soll dann dem Anwender gesagt werden? Am besten etwas, was er falsch gemacht hat, und wie er es besser machen soll. Für den Entwickler sollte am besten auch noch eine Information rausspringen, wo konkret der Fehler entstanden ist.

In einer Exception steckt all diese Information drin, in einem booleeschen Rückgabewert nicht.

Zweitens: in aller Regel können ein paar Zeilen Code nicht nur eine, sondern unzählige Exceptions auslösen. Bei "Parse" ist das noch recht übersichtlich, aber bereits in deinem Beispiel stecken noch ein paar Aufrufe mehr drin, die potentielle Exceptions auslösen könnten, wie z.B. NullReferenceExceptions.
Du brauchst also ohnehin Exception Handling in deinem Code - dann kannst du es auch gleich richtig machen, und kannst dir die expliziten Prüfungen sparen.

Matrix316
2010-08-18, 12:09:02
[...]

Convert.ToDecimal. Und das ist im Standard .NET Framework drin, ist also grundsätzlich mal von allen .NET Sprachen aus aufrufbar - also auch ASP.NET

Meine Aussage war eher auf das von oben bezogen:


objekttyp.zahl = TextBoxBlaBlubb.Text.ToDecimal();

PatkIllA
2010-08-18, 12:11:44
Vielleicht eine Extension Method? Ich wüsste aber auch keine, die im Framework drin ist, die ToDecimal() auf strings ergänzt.

Matrix316
2010-08-18, 12:27:40
Das mein ich. Decimalvariable.ToString() gibts, aber Stringvariable.ToDecimal() wäre schön, wenns sowas geben würde. ;)

PatkIllA
2010-08-18, 12:30:17
Bloss nicht
das führt nur zu noch mehr Fehlern durch missachten der Culture.

Matrix316
2010-08-18, 12:56:39
Cultura hab ich beim "Casten" noch nie beachtet. Hab ich durch den Thread auch zum ersten Mal so gesehen. :D

RattuS
2010-08-18, 13:01:11
Erstens: dein "If...Then" alleine reicht ja nicht. Du musst auch noch irgendwie angemessen auf diesen Fehler reagieren. Was soll passieren? In eine Log Datei schreiben? Eine Message Box werfen? Was genau soll dann dem Anwender gesagt werden? Am besten etwas, was er falsch gemacht hat, und wie er es besser machen soll. Für den Entwickler sollte am besten auch noch eine Information rausspringen, wo konkret der Fehler entstanden ist.

In einer Exception steckt all diese Information drin, in einem booleeschen Rückgabewert nicht.

Zweitens: in aller Regel können ein paar Zeilen Code nicht nur eine, sondern unzählige Exceptions auslösen. Bei "Parse" ist das noch recht übersichtlich, aber bereits in deinem Beispiel stecken noch ein paar Aufrufe mehr drin, die potentielle Exceptions auslösen könnten, wie z.B. NullReferenceExceptions.
Du brauchst also ohnehin Exception Handling in deinem Code - dann kannst du es auch gleich richtig machen, und kannst dir die expliziten Prüfungen sparen.
Also bitte, mein Beispiel war doch nur ein Ausschnitt. Ich fang doch nicht an in diesem Forum umfangreiche Code-Snippets zu schreiben. Natürlich geht man auf Falscheingabe ein, nur das widerrum ist eine Angelegenheit, die per Control-Input-Validierung gelöst werden sollte. Auf der Datenebene sollten Falscheingaben nur noch geblockt und ggf. umgeleitet werden. Controls/Dialogs haben in einer Datenmethode absolut nichts zu suchen. Das sind aber auch Grundlagen, die du, so denke ich doch, sicherlich kennst.

Der Entwickler muss grundsätzlich alle Fehlerfälle abdecken (und dafür gibt es bei .NET/VS genügend Tools), ergo ist eine Protokollierung interner Fehler nur bedingt sinnvoll, nämlich nur dann, wenn die Fehlerquellen nicht abschätzbar sind. Gerade bei Exceptions kann man diese Dinge aber oftmals vermeiden. NullreferenceExceptions, also in meinem Snippets das Control oder das eigene Object, sind prinzipiell vor der Verarbeitung zu prüfen. Das ist eine Frage des internen Designs. Exception-Handling ist erst dann erforderlich, wenn Fehlerquellen unausweichlich sein können, z.B. externe Zugriffseinschränkungen beim gesamten System.IO-Namespace. DAS sind Exceptions, die man behandeln muss, weil es keinen Weg zur Sicherstellung des Zugriffes gibt.

PatkIllA
2010-08-18, 13:36:46
Cultura hab ich beim "Casten" noch nie beachtet. Hab ich durch den Thread auch zum ersten Mal so gesehen. :D
Dann hast du hoffentlich noch nie Daten abgespeichert.

Monger
2010-08-18, 13:50:13
Der Entwickler muss grundsätzlich alle Fehlerfälle abdecken (und dafür gibt es bei .NET/VS genügend Tools), ergo ist eine Protokollierung interner Fehler nur bedingt sinnvoll, nämlich nur dann, wenn die Fehlerquellen nicht abschätzbar sind. Gerade bei Exceptions kann man diese Dinge aber oftmals vermeiden.

Keine größere, moderne Software ist fehlerfrei, und du kannst unmöglich alle Fälle prüfen. Da Software auch immer lebt, treten auch gerne mal an Stellen Fehler auf, wo sich der Code gar nicht geändert hat, sondern nur die Programmbibliotheken auf die er verweist.
Natürlich kann man einiges abfangen. Mit gutem Design kann man Fehler reduzieren. Und natürlich ist es empfehlenswert, Methodenparameter auf Gültigkeit zu prüfen, wenn man schonmal weiß dass dort Probleme auftauchen können. Aber spätestens dann wird der Aufwand unverhältnismäßig, wenn du versuchst jeden Scheiß zu berücksichtigen. Im einfachsten Fall macht man einfach gar nichts mit den Exceptions, und lässt sie einfach bis zur .NET Runtime durchrasseln (ein Crash ist nicht selten das kleinere Übel), oder man versucht, angemessen in bestimmten Bereichen auf Exceptions zu reagieren.


Wo kommt eigentlich diese Phobie vor Exceptions her? Ich seh auch auf Arbeit regelmäßig diese "Try... Catch{return false;}" Konstrukte. Lieber eine Exception zu viel als zu wenig geworfen.

Matrix316
2010-08-18, 15:16:23
Dann hast du hoffentlich noch nie Daten abgespeichert.
Doch, jede Menge schon. Hab aber immer drauf geachtet, dass sie auch richtig abgespeichert werden und nicht mich auf Culture verlassen. Und wie man hier sieht, hilft es auch nicht immer. Normal ist es nämlich keine so große Sache Decimal Werte abzuspeichern. Aber ich glaube hier war eher beim DB Feld selbst was falsch.

PatkIllA
2010-08-18, 15:17:38
Doch, jede Menge schon. Hab aber immer drauf geachtet, dass sie auch richtig abgespeichert werden und nicht mich auf Culture verlassen. Und wie man hier sieht, hilft es auch nicht immer. Normal ist es nämlich keine so große Sache Decimal Werte abzuspeichern. Aber ich glaube hier war eher beim DB Feld selbst was falsch.
Wie hast du sie denn "richtig" abgespeichert? Und natürlich kann man sich auf Culture verlassen. Es gibt ja extra die InvariantCulture.

Matrix316
2010-08-18, 15:21:11
Naja, ich kuck nach, ob der Wert, den ich in der Textbox oder wo auch immer eingebe, auch am Ende in der Datenbank steht. ;)

Wobei ich erinnere mich dunkel Culture mal verwendet zu haben, und ich meine beim Farpoint Spread um dafür zu sorgen, dass die Eingaben richtig übernommen werden. Ansonsten bei Textboxen oder sonstwie Strings in Decimal zu konvertieren noch nie.

Bei Textboxen ist da ein Regular Expression Validator auch ganz hilfreich um die Eingabe korrekt zu halten.

stav0815
2010-08-18, 15:30:19
Man kann ja schon beim abspeichern in die Decimal dafür sorgen, dass sie nur auf eine bestimmte art und weise gespeichert wird.

PatkIllA
2010-08-18, 15:35:03
Naja, ich kuck nach, ob der Wert, den ich in der Textbox oder wo auch immer eingebe, auch am Ende in der Datenbank steht. ;)
:facepalm:

Bei Textboxen ist da ein Regular Expression Validator auch ganz hilfreich um die Eingabe korrekt zu halten.Und da baust du dir für jede Culture eigene Expressions zusammen? Du glaubst gar nicht was alles sprachabhängig ist, bzw vom Benutzer eingestellt werden kann. Außerdem ist es auch immer doof, wenn Programme nicht die eingestellte Formatierung benutzen sondern meinen mir ein Datumsformat aufzwingen zu müssen.

Matrix316
2010-08-18, 16:32:11
Die User haben das zu machen, was wir ihnen vorschreiben. ;) Unsere Programme nutzen normalerweise nur Benutzer aus Deutschland und auch nur ausgewählte. ;)