1

I want to return values from database. I defined table where each columns has defined Type of data:

public enum TTableOffers
{
    [CStringValue("id")]
    [CType(typeof(Integer))]
    Id = 0,
    [CStringValue("project")]
    [CType(typeof(String))]
    ProjectId = 1,

}

So column "id" is type Integer, "project" is String etc. Now I want to get value from specific column by this enum like this:

    public T Value<T>(Enum enumColumn) where T : class // <<< class!
    {
        object aResult = m_aSqlDataReader.GetValue(columnIndex); // Here the aResult is filled with correct value (object) from database (working fine)

        ...     

        Type colType = CTypeAttribute.GetTypeValue(enumColumn); // Read the type from table - Integer or String (this is working fine)

        if (aResult == null) 
            return default(T);
        else
        {
           if (Convert.IsDBNull(aResult))
              return default(T);
           else
              aResult = ChangeType(aResult, colType); // Change the type to desired one --- here is the crash!!!                             
        }

        return (T)aResult;
       // here I also tried: return aResult as T;
    }

As method ChangeType I use this one: Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Int32, mscorlib]].

Column with type String are working fine, but with Integer there is a problem.

As I cannot use Int32 (which is struct) so I defined my own class Integer like this:

public class Integer : IComparable, IFormattable, IConvertible, IComparable<Integer>, IEquatable<Integer>
{
    int value = 0;

    public Integer(int value)
    {
        this.value = value;
    }

    // (Integer)123
    public static implicit operator Integer(int value)
    {
        return new Integer(value);
    }

    // (int)myInteger
    public static implicit operator int(Integer integer)
    {
        if (integer == null)
            integer = new Integer(default(int));

        return integer.value;
    }

    public static int operator +(Integer one, Integer two)
    {
        return one.value + two.value;
    }

    public static Integer operator +(int one, Integer two)
    {
        return new Integer(one + two);
    }

    public static int operator -(Integer one, Integer two)
    {
        return one.value - two.value;
    }

    public static Integer operator -(int one, Integer two)
    {
        return new Integer(one - two);
    }

    public static bool operator >(Integer one, int two)
    {
        return (int)one > two;
    }

    public static bool operator <(Integer one, int two)
    {
        return (int)one < two;
    }

    public static bool operator >(int one, Integer two)
    {
        return one > (int)two;
    }

    public static bool operator <(int one, Integer two)
    {
        return one < (int)two;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public byte ToByte(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public char ToChar(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public DateTime ToDateTime(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public decimal ToDecimal(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public double ToDouble(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public short ToInt16(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public int ToInt32(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public long ToInt64(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public sbyte ToSByte(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public float ToSingle(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public string ToString(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public object ToType(Type conversionType, IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public ushort ToUInt16(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public uint ToUInt32(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public ulong ToUInt64(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }

    public int CompareTo(object obj)
    {
        throw new NotImplementedException();
    }

    public string ToString(string format, IFormatProvider formatProvider)
    {
        throw new NotImplementedException();
    }

    public int CompareTo(Integer other)
    {
        throw new NotImplementedException();
    }

    public bool Equals(Integer other)
    {
        throw new NotImplementedException();
    }
}

For String everything is working fine but for Integer I got "Invalid cast from 'System.Int32' to 'Code.Data.Integer'." exception.

I set breakpoints on EVERY line in class Integer and no one is hit. I assume I am missing something important in class Integer as it is never hit and no conversion is done.

What am I missing?

Community
  • 1
  • 1
Slappy
  • 5,250
  • 1
  • 23
  • 29
  • I think it's a bad idea to use attributes in this scenario, as far as return type goes. If you do your generic method right then its own signature will give you typing info already. No need to use reflection on every call for something you already know. :) – Crono Feb 19 '14 at 14:57

2 Answers2

1

Your problem is that your value, in this case an int, does not implement the IConvertible interface. From the MSDN documentation:

ChangeType is a general-purpose conversion method that converts the object specified by value to conversionType. The value parameter can be an object of any type, and conversionType can also be a Type object that represents any base or custom type. For the conversion to succeed, value must implement the IConvertible interface, because the method simply wraps a call to an appropriate IConvertible method. The method requires that conversion of value to conversionType be supported.

Additionally, from the documentation on IConvertible:

If you implement the IConvertible interface, your implementation will be called automatically by the Convert.ChangeType method if the Object parameter is an instance of your implementing type and the Type parameter is a common language runtime type.

You are calling this in the reverse fashion: going from a CLR type (int) to your custom type, which is not going to work.

Sven Grosen
  • 5,616
  • 3
  • 30
  • 52
  • You are probably right, I could solve it in no way so I created separate method for each type, thanks. – Slappy Feb 20 '14 at 05:23
0

Why don't you make two Value method like this:

public Nullable<T> Value<T>(Enum enumColumn) where T : struct {...}

public string Value(Enum enumColumn) {...}

The first one will allow you to return null integers among others.

Much cleaner, no? :)

Crono
  • 10,211
  • 6
  • 43
  • 75
  • I ended with creating separate methods for each type like .GetString(Enum enumColumn) .GetInt(Enum enumColumn), GetDateTime(... , but thanks anyway. – Slappy Feb 20 '14 at 05:23
  • No problem. :) Out of curiosity, have you met any troubles with the Nullable struct type approach? I've been using this myself for a few years now and it works flawlessly for me. It supports decimal, short, int, datetime, long, guid, float and char all in one place. The only exception I had to make is string. – Crono Feb 20 '14 at 13:13