Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc
Titulní Blogy Fotky Soubory

MAFův .NETí deníček

zajímavosti ze světa .NETu ...

  • Konverze hodnot typu SqlDecimal na Decimal

    Tak tu mám další kontroverzní příspěvek. Kolega mi nedávno hlásil nepříjemný, datově závislý problém s konverzí hodnot typu SqlDecimal na typ Decimal.

    V čem, že spočívá problém ?
    Typ System.Decimal má max. přesnost 29 míst, zatímco typ System.Data.SqlTypes.SqlDecimal 38 míst.

    Bohužel, zřejmě i takovéto hodnoty se nám občas nacházejí v databázi a bezpečně je ošetřit není úplně "easy", na property sqlDecimal.Value zapomeňte.

    .NET Framework se v tomto případě při konverzi chová přísně logicky, vyvolá vyjímku typu Conversion overflow, tzn. odmítne hodnotu zaokrouhlit, zkrátit či jinak "zmrzačit".

    Na druhou stranu je velmi zajímavé, že konstruktor typu sql decimal vám z reálného čísla 10e-20 vyrobí bez problémů sql decimal s hodnotou 0 a přesností 17 míst. Čemuž teda přesně nerozumím :-(

    Kolegovi jsem poradil, aby rozhodně zvážil, jak přesná čísla vlastně chce do databáze ukládat a zda je po načtení z databáze za těchto podmínek vůbec chceme někam konvertovat. No, stejně radši bacha na property sqlDecimal.Value pro konverzi hodnoty na hodnotu typu decimal.

    Pokud si myslíte, že .Net framework obelstíte např. použitím metody Convert.ToDecimal, pak vás taky zklamu, ta pro sql typy nefunguje vůbec.

    Našel jsem jen jediný způsob: převzorkovat hodnotu na hodnotu s nižší přesností.
    A nyní příklad:

    using System.Data.SqlTypes;
    
    //k naplneni sql decimalu pouzijeme napr. hodnotu PI na 35 desetinnych mist (kterou kazdy znate jeste ze skoly ;-)) SqlDecimal dec1 = SqlDecimal.Parse("3.14159265358979323846264338327950288"); decimal dec2 = dec1.Value; // --> Conversion Overflow exception decimal dec3 = SqlDecimalValue(dec1); //funguje a cislo "prevzorkuje" na nizsi presnost
    /// <summary> /// Safe converter from SqlDecimal to Decimal value (null -> 0). /// </summary> /// <param name="sqlDecimal">SqlDecimal value (precision 38)</param> /// <returns>Decimal value (precision 29)</returns> public static decimal SqlDecimalValue(SqlDecimal sqlDecimal) { //osetreni null hodnoty, 'sqlDecimal.Value' standardne vraci System.Data.SqlTypes.SqlNullValueException if (sqlDecimal.IsNull) return 0M;
    //osetreni preteceni presnosti decimalu, 'sqlDecimal.Value' standardne vraci System.OverflowException const int precDecimal = 29; //presnost decimalu, lze ho urcit napr. takto: new SqlDecimal(Decimal.MaxValue).Precision; if (sqlDecimal.Precision <= precDecimal) return sqlDecimal.Value;
    //delsi cislo typu SqlDecimal je nutne prevzorkovat na nizsi presnost typu Decimal int scaleDecimal = precDecimal - (sqlDecimal.Precision - sqlDecimal.Scale); SqlDecimal convertedDecimal = SqlDecimal.ConvertToPrecScale(sqlDecimal,precDecimal,scaleDecimal); return convertedDecimal.Value; }

    Lze předpokládat, že obdobný problém může nastat také pro příliš vysoká čísla, ovšem pro takový případ lék nemám, změnu hodnoty na 30-tém místě zamaskuju, ale zkrácením čísla o několik řádů se uživateli rozhodně nezavděčím ;-)

  • Nedoceněný dekorátor IExtenderProvider

    Tento článeček je určen těm z vás, kteří zatím nevězíte až po krk ve WPF  ;-)

    IExtenderProvider je velmi zajímavé a užitečné rozhraní v namespace System.ComponentModel.

    Pokud vytváříte složitější WinForm nebo WebForm aplikaci snadno můžete narazit na problém, jak k použitým vizuálním controlům přidat několik společných vlastností. Obvykle tuto potřebu řešíte děděním, tj. vytvořením vlastní vrstvy vizuálních controlů a "nacpáním" vaší společné logiky do všech těchto controlů.

    Avšak v mnoha případech stačí použít IExtenderProvider a implementovat vaši společnou logiku pomocí jediné pomocné třídy. Připomeňme pro zajímavost několik tříd .Net frameworku implementujících toto rozhraní jako např. ErrorProvider, HelpProvider nebo ToolTip.

    Několik příkladů naleznete na Code Projectu, názorný příkládek jsem nalezl také zde: http://www.log-2.net/spot/enter-jako-tabulator.aspx

    Připravil jsem také jednoduchý okomentovaný příklad:

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    
    //seznam vsech property poskytovanych extenderem rozsirujici uvedene typy controlu [ProvideProperty("MyString", typeof(Control))] //je rozumne podedit extender od tridy Component, //aby se objevil na liste nevizualnich komponent v designeru Visual Studia public class MyExtender: Component, IExtenderProvider { //sem se ukladaji hodnoty vsech property vsech controlu, //klicem je typ podporovaneho controlu a hodnotou pomocna trida Properties private Hashtable properties; #region Public contructors public MyExtender() { properties = new Hashtable(); } public MyExtender(IContainer container): this() { container.Add(this); } #endregion #region IExtenderProvider Members /// <summary> /// Can extend Controls /// </summary> /// <param name="extendee"></param> /// <returns>Gets info about can extend object</returns> public bool CanExtend(object extendee) { //typy controlu podporovane extenderem vraci true if (extendee is Control) { return true; } return false; } #endregion
    #region MyString - implementace property [DefaultValue("")] [Description("This string will be very useful when you use it.")] [Category("MyCategory")] public string GetMyString(Control c) { //precteni hodnoty z extenderu return EnsurePropertiesExists(c).MyString; }
    public void SetMyString(Control c, string value)
    { //vlozeni hodnoty do extenderu EnsurePropertiesExists(c).MyString = value; if(this.DesignMode) { //sem muzete vlozit logiku, ktera ma probehnout pouze v design modu } else { //sem muzete vlozit logiku, ktera ma probehnout pouze v runtime modu } } #endregion #region Pomocné objekty a metody //pomocna trida pro uchovani hodnot vsech property poskytovanych extenderem private class Properties { public string MyString; //... public Properties() { MyString = String.Empty; //... } }
    //pomocna metoda pro vkladani a vyber hodnot private Properties EnsurePropertiesExists(object key) { Properties p = (Properties) properties[key]; //automaticky vytvari instanci property, pokud je potreba if( p == null ) { p = new Properties(); properties[key] = p; } return p; } #endregion }
  • Můj milý deníčku ...

    Ahoj,
    málem jsem zapomněl na uvítací příspěvek.

    Vítám vás na stránkách svého deníčku.

    Chtěl bych zde prezentovat své osobní zážitky, ale zejména propagovat programátorské umění a techniky eXtrémního programování. Především v prostředí technologie .NET a jazyka C#.

    zdraví vás MAF

     

  • Jak správně zkonvertovat bytové pole na string a zpět

    Obrátil se na mne kolega s dotazem, jak správně zkonvertovat obecné bytové pole na string a zpět. Že používá pro konverzi třídu System.Text.Encoding, a že tomu nerozumí, protože mu to vrací pro různá kódování různé jiné hodnoty než vložil. Řekl jsem si, že by chyba frameworku ? Vždyť konverze pomocí System.Text.Encoding přeci musí fungovat obousměrně.

    Příklad:
    string stringBase64 = "AAEAAAD/////AQAAAAAAAAAMAgAAAEFM
    Q1MuU2hhcmVkLCBWZXJzaW9uPTEuMC4wLjAsIEN1bHR1cmU9bmV1dHJhbCwgUHV
    ibGljS2V5VG9rZW49bnVsbAUBAAAAJE5vcmlzLldTLkFjdGlvbnMuQnJvd3NlK0
    9uUm93Q2hhbmdlZA8AAAAFVmFsdWUIQnJvd3NlSUQGQWN0aW9uBk5hdkFyZwpPd
    Ghlckl0ZW1zC090aGVyVmFsdWVzC090aGVyVGFibGVzEUVkaXRCcndUYWJsZU5h
    bWVzDkVkaXRCcndEZk5hbWVzEEVkaXRCcndDb2xzV2lkdGgPRWRpdEJyd0NvbHN
    MaXN0BlBhZ2VJRA5TZW5kRXh0ZW5zaW9ucwxNZXNzYWdlQ29kZXMLV2luZG93c0
    luZm8BAQEBBgYGBgYGBgAABgYIAQIAAAAGAwAAABZsYXN0Um93PSI1IiBuZXdSb
    3c9IjMiBgQAAAAIbXlCcm93c2UGBQAAAAAJBQAAAAoKCgoKCgruAwAAAQoKCw==";

    byte[] byteArray = System.Convert.FromBase64String(stringBase64);

    // utf8 nefunguje
    string s8 = System.Text.Encoding.UTF8.GetString(byteArray);
    byte[] b8 = System.Text.Encoding.UTF8.GetBytes(s8);

    // utf16 funguje
    string s16 = System.Text.Encoding.Unicode.GetString(byteArray);
    byte[] b16 = System.Text.Encoding.Unicode.GetBytes(s16);


    A teď řešení, ke kterému jsem dospěl:

    On je totiž velký rozdíl, v jakém směru je prováděna konverze, zda ve směru string -> byte[] -> string a nebo naopak.

    // utf8 (string -> byte[] -> string ... funguje)
    byte[] bb8 = System.Text.Encoding.UTF8.GetBytes(stringBase64);
    string ss8 = System.Text.Encoding.UTF8.GetString(bb8);

     a nebo takhle:

    // utf8 (byte[] -> string -> byte[] ... nefunguje)
    string ss8 = System.Text.Encoding.UTF8.GetString(byteArray);
    byte[] bb8 = System.Text.Encoding.UTF8.GetBytes(ss8);

    Proč !?
    Protože záleží na přípustném definičním oboru vstupních hodnot, zatímco první případ funguje vždy (pro jakékoli kódování stoprocentně) z důvodu, že jakýkoli unicode znak lze převést na jeho bytovou podobu, tak druhý případ umí bezchybně zkonvertovat na znaky pouze bytové pole s hodnotami platnými pro dané kódování. A protože pro UTF-8 nejsou některé bytové kombinace přípustné (nemají přiřazen žádný odpovídající znak), je výsledek dekódován špatně. Dtto podobný výsledek dostaneme i pro ASCII7 a UTF-32. Z obecného bytového pole konvertuje správně pouze UTF-16, neboť pro něj jsou přípustné všechny 2-bytové kombinace.

    Jaké máme další možnosti konverze:

    napadá mě System.Convert.ToBase64String a System.Convert.FromBase64String ... výsledkem konverze je ale cca trojnásobně veliký string :-(

    Máte nějaké další nápady ? Sem s nimi ...

Powered by Community Server (Personal Edition), by Telligent Systems
Vyvojar.cz na prodej!