none
Linq to SQL: Mapping String -> Double RRS feed

  • Frage

  • Hallo,

    ich möchte in (A)Linq to SQL einen String auf Double mappen. Das funktioniert solange, wie keine Fehlerbehandlung erforderlich ist, sprich alle in der Spalte vorhandenen Werte sich problemlos in einen Double Wert parsen lassen.

    private string _value;
    [ALinq.Mapping.Column(Storage = "_value")]
    public double value
    {
      get
      {
        return double.Parse(_value);
      }
    }
    

    Soll jetzt auch ein Eintrag wie "+" oder "k.a." abgefangen und als Wert 0 geleifert werden, wird dies komplett ignoriert und es kommt eine FormatException, egal, ob vor der Umwandlung mittels RegEx geprüft wird, ich einen try/catch verwende oder explizit auf + oder k.a. prüfe.
    Wo liegt mein Denkfehler?

    Grüße
    Anja

    Dienstag, 28. September 2010 19:38

Antworten

  • Hallo Anja,

    Das Problem ist nicht ALinq-spezifisch, sondern hängt mit der Linq-Infrastruktur zusammen. Die FormatException ereignet sich zur Laufzeit, bei der Iteration über IQueryable<T>. Da der Ausnahmefehler schon bei der Materialisierung des jeweiligen Iterations-Objekts geworfen wird, lange vor dem Aufruf von [Entität].Value, bringt es auch nichts, die Umwandlungslogik in den Getter zu packen. Die Ausführung erreicht diesen Punkt nicht.

    Wenn ich von der wahrscheinlich nicht möglichen datenbankseitigen Sanitisierung der Daten absehe, gäbe es noch folgende Möglichkeit:

    [Column(Storage="_Value", DbType="NVarChar(50)", UpdateCheck=UpdateCheck.Never)]
    public string Value
    {
      get { return _Value; }
    }
    
    public double DoubleValue
    {
      get
      {
        double result = 0d;
        double.TryParse(this.Value, out result);
        return result;
      }
    }
    

    Dabei verwendest Du einfach eine Wrapper-Property statt der originalen Property. Jetzt sollte auch die Iteration, bzw. die Materialisierung kein Problem mehr sein und Du erhältst trotzdem den gewünschten Wert zum Anzeigen:

    var conn = @"Data Source=.;Initial Catalog=TestDb;Integrated Security=True";
    var db = new ALinq.DataContext(new SqlConnection(conn), typeof(ALinq.SqlClient.Sql2005Provider));
    var q = from s in db.GetTable<TestAnja>()
        select s;
    
    foreach (var t in q)
      Console.WriteLine(t.DoubleValue);
    
    Console.ReadKey(true);
    

    Gruß
    Marcel

    • Als Antwort markiert Anja Länge Montag, 18. Oktober 2010 18:37
    Montag, 11. Oktober 2010 07:46
    Moderator
  • Hallo Anja,

    Du kannst zum Beispiel eine pragmatische Methode benutzen, die gleich typische "," / "." Problematiken mit abfängt.
    In Deinem Beispiel würdest Du dann:

         return ToDouble(_value);

    benutzen.
    ____________

      private void Form1_Load(object sender, EventArgs e)
      {
       string[] beispielStrings = new string[] { "+", "k.a.", "1.234", "1,234" };
    
       foreach (var s in beispielStrings)
       {
        double dbl = ToDouble(s);
        MessageBox.Show(dbl.ToString());
       }
      }
    
      /// <summary>Wandelt in String in Double um</summary>
      public static double ToDouble(string dblString)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
       return double.Parse(dblString, CultureInfo.InvariantCulture);
      }
    
      private static bool Spezialfall(out double dbl, string dblString)
      {
       if (dblString.Trim() == "+") {dbl = 0; return true;}
       if (dblString.Trim() == "k.a.") {dbl = 0; return true;}
       dbl = 0; return false;
      }
    
      /// <summary>Wandelt String in double um,
      /// gibt 'standardWert' zurück, falls es nicht möglich ist.
      /// </summary>
      public static double ToDouble(string dblString, double standardWert)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
    
       try
       {
        dblOut = double.Parse(dblString, CultureInfo.InvariantCulture);
       }
       catch
       {
        dblOut = standardWert;
       }
    
       return dblOut;
      }
    
      /// <summary>
      /// Wandelt einen String in Double um (Default = 0 
      /// oder es wird eine Exception ausgelöst)
      /// </summary>
      public static double ToDouble(string dblString, bool ausnahmeWerfen)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
    
       try
       {
        dblOut = double.Parse(dblString, CultureInfo.InvariantCulture);
       }
       catch (Exception ex)
       {
        if (ausnahmeWerfen) throw ex;
        else dblOut = 0;
       }
    
       return dblOut;
      }
    

    ciao Frank
    Mittwoch, 29. September 2010 07:21

Alle Antworten

  • Hallo Anja,

    Du kannst zum Beispiel eine pragmatische Methode benutzen, die gleich typische "," / "." Problematiken mit abfängt.
    In Deinem Beispiel würdest Du dann:

         return ToDouble(_value);

    benutzen.
    ____________

      private void Form1_Load(object sender, EventArgs e)
      {
       string[] beispielStrings = new string[] { "+", "k.a.", "1.234", "1,234" };
    
       foreach (var s in beispielStrings)
       {
        double dbl = ToDouble(s);
        MessageBox.Show(dbl.ToString());
       }
      }
    
      /// <summary>Wandelt in String in Double um</summary>
      public static double ToDouble(string dblString)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
       return double.Parse(dblString, CultureInfo.InvariantCulture);
      }
    
      private static bool Spezialfall(out double dbl, string dblString)
      {
       if (dblString.Trim() == "+") {dbl = 0; return true;}
       if (dblString.Trim() == "k.a.") {dbl = 0; return true;}
       dbl = 0; return false;
      }
    
      /// <summary>Wandelt String in double um,
      /// gibt 'standardWert' zurück, falls es nicht möglich ist.
      /// </summary>
      public static double ToDouble(string dblString, double standardWert)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
    
       try
       {
        dblOut = double.Parse(dblString, CultureInfo.InvariantCulture);
       }
       catch
       {
        dblOut = standardWert;
       }
    
       return dblOut;
      }
    
      /// <summary>
      /// Wandelt einen String in Double um (Default = 0 
      /// oder es wird eine Exception ausgelöst)
      /// </summary>
      public static double ToDouble(string dblString, bool ausnahmeWerfen)
      {
       double dblOut; if (Spezialfall(out dblOut, dblString)) return dblOut;
       dblString = dblString.Replace(",", ".");
    
       try
       {
        dblOut = double.Parse(dblString, CultureInfo.InvariantCulture);
       }
       catch (Exception ex)
       {
        if (ausnahmeWerfen) throw ex;
        else dblOut = 0;
       }
    
       return dblOut;
      }
    

    ciao Frank
    Mittwoch, 29. September 2010 07:21
  • Hallo,

    das funktioneirt in einem normalen Programmablauf, aber leider nicht, wenn der Double-Wert eine Eigenschaft eines Objektes ist, das ich mittels ALinq aus einer DB erstelle. Wie bei meinen anderen Versuchen vorher, bei denen ich ja auch bereits die Sonderfälle berücksichtigt hatte, läuft dann die FormatException auf. Ist das ein Problem speziell von ALinq oder generell in den Linq to xy Lösungen?

    Grüße
    Anja

    Sonntag, 10. Oktober 2010 17:27
  • Hallo Anja,

    Das Problem ist nicht ALinq-spezifisch, sondern hängt mit der Linq-Infrastruktur zusammen. Die FormatException ereignet sich zur Laufzeit, bei der Iteration über IQueryable<T>. Da der Ausnahmefehler schon bei der Materialisierung des jeweiligen Iterations-Objekts geworfen wird, lange vor dem Aufruf von [Entität].Value, bringt es auch nichts, die Umwandlungslogik in den Getter zu packen. Die Ausführung erreicht diesen Punkt nicht.

    Wenn ich von der wahrscheinlich nicht möglichen datenbankseitigen Sanitisierung der Daten absehe, gäbe es noch folgende Möglichkeit:

    [Column(Storage="_Value", DbType="NVarChar(50)", UpdateCheck=UpdateCheck.Never)]
    public string Value
    {
      get { return _Value; }
    }
    
    public double DoubleValue
    {
      get
      {
        double result = 0d;
        double.TryParse(this.Value, out result);
        return result;
      }
    }
    

    Dabei verwendest Du einfach eine Wrapper-Property statt der originalen Property. Jetzt sollte auch die Iteration, bzw. die Materialisierung kein Problem mehr sein und Du erhältst trotzdem den gewünschten Wert zum Anzeigen:

    var conn = @"Data Source=.;Initial Catalog=TestDb;Integrated Security=True";
    var db = new ALinq.DataContext(new SqlConnection(conn), typeof(ALinq.SqlClient.Sql2005Provider));
    var q = from s in db.GetTable<TestAnja>()
        select s;
    
    foreach (var t in q)
      Console.WriteLine(t.DoubleValue);
    
    Console.ReadKey(true);
    

    Gruß
    Marcel

    • Als Antwort markiert Anja Länge Montag, 18. Oktober 2010 18:37
    Montag, 11. Oktober 2010 07:46
    Moderator
  • Hallo Marcel,

    jup, das funktioniert. Danke.

     

    Anja

    Montag, 18. Oktober 2010 18:36