none
GENERIC METHOD TO CONVERT SYSTEM.DBNULL TO NULL RRS feed

  • Question

  • regards,
    I am trying to create a generic function that allows me to convert null values from the database to a null object in the c # code
    This is what I have so far, but the problem is that if I enter a value as a decimal, it returns 0 by default and not null

         public static T GetNullable<T>(object valor)
            {
                return System.Convert.IsDBNull(valor) ? default(T) : (T)valor;//
            }

     fase.Amount=GetNullable<decimal>(myReader["Amount"]);

    I get 0

    Wednesday, September 18, 2019 3:32 PM

All replies

  • There are different ways to handle this depending upon your needs.

    //I use a helper type that handles conversion and ultimately it does the equivalent of this - converted to your naming convention
    public static T GetNullable<T> ( object value )
    {
       if (value == null || value == DBNull.Value)
          return default(T);
    
       if (value is string str)
          return (T)Convert.ToType(typeof(T), str);
    
       return default(T);
    }
    However you said the problem is that you get back 0 for decimal and not null. Decimal is a value type so you wouldn't get back null. It's default value is 0 just like all the other floating point types. Why would you expect null here?


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, September 18, 2019 3:48 PM
    Moderator
  • the problem is that if I enter a value as a decimal, it returns 0 by default and not null

    It is not possible to store a null inside a Decimal because the Decimal is a value-type and value-types do not accept nulls. Null can only be assigned to reference-types.

    You can convert a value-type into a reference-type by using Nullable<T>. For example, in the case of a Decimal you can use instead a Nullable<Decimal>. The C# compiler also lets you specify this as decimal? .

    Wednesday, September 18, 2019 4:10 PM
    Moderator
  • Hi Alberto

    ...."BTW, I find it more compact to use "as" instead of IsDbNull:
    
    public static T GetNullable<T>(object valor) where T:class
    {
        return valor as T;
    }"......

    With that implementation, now I get the error

    The type 'decimal' must be a reference type in order to use it as parameter 'T' in the generic type or method GetNullable<T>(object)


    • Edited by Augusto C Wednesday, September 18, 2019 4:19 PM
    Wednesday, September 18, 2019 4:18 PM
  • Hi CoolDadTx

     if (value is string str)
          return (T)Convert.ToType(typeof(T), str);

    Convert' does not contain a definition for 'ToType'

    The first If is very similar to what I did

    • Edited by Augusto C Wednesday, September 18, 2019 4:25 PM
    Wednesday, September 18, 2019 4:23 PM
  • Sorry, tried to map to what was in the framework. Perhaps something simpler.

    return (T)Convert.ChangeType(typeof(T), value);

    Unfortunately that doesn't work well if the types aren't compatible. Something more flexible would be to use TypeConverter.

    class Program
    {
        static void Main ( string[] args )
        {
            var results = new[] {
                GetNullable<decimal>(null),
                GetNullable<decimal>(DBNull.Value),
                GetNullable<decimal>(45.68M),
                GetNullable<decimal>(4.567),
                GetNullable<decimal>("12345"),
                GetNullable<decimal>("abc")
                };
        }
    
        public static T GetNullable<T> ( object value )
        {
            if (value == null || value == DBNull.Value)
                return default(T);
    
            if (value.GetType() == typeof(T))
                return (T)value;
    
            var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T));
    
            try
            {                
                return (T)converter.ConvertFromString(value.ToString());
            } catch (Exception e) when (IsIgnorableException(e))
            {
            };
    
            return default(T);
        }
    
        private static bool IsIgnorableException ( Exception error )
        {
            if (error is FormatException || error is NotSupportedException || error is ArgumentException || error is InvalidCastException)
                return true;
    
            //Some converters return an Exception that contains the inner exception from above
            if (error.InnerException != null && IsIgnorableException(error.InnerException))
                return true;
    
            return false;
        }
    }


    Michael Taylor http://www.michaeltaylorp3.net

    Wednesday, September 18, 2019 5:49 PM
    Moderator
  • The type 'decimal' must be a reference type

    Exactly. That's the reason why we use "where T:class". This tells the compiler that it must only accept reference-types, and not value-types, which can not contain null and therefore cannot be used to return null from this function.

    In this way, the compiler issues an error that tells you that you have made again the mistake of using a decimal instead of a Nullable<decimal>.

    Wednesday, September 18, 2019 7:49 PM
    Moderator
  • Hi Augusto C, 

    Thank you for posting here.

    According to your question, I make a test on my side. I insert DBnull and 23 type of decimal into my table, and reproduce your problem.

    As Alberto Poblacion suggested, you need to replace decimal with Nullable<Decimal>.

         fase.Amount=GetNullable<Nullable<decimal>>(myReader["Amount"]);

    Result of my test:


    Hope it can help you.

    Best Regards,

    Xingyu Zhao


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, September 19, 2019 9:06 AM
    Moderator