I have made a conversion method for handling the database values returned by procs. It looks like this:
public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
object value = record[index];
if (value is string)
value = string.IsNullOrEmpty(value as string) ? null : value;
Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
if (nullableUnderlyingType != null)
{
if (nullableUnderlyingType.IsEnum)
return value == null || value.Equals(DBNull.Value) ? default(T) : (T)Enum.ToObject(nullableUnderlyingType, value);
}
/*
//This is my try on solving the problem, but won't compile
//because T has no struct constraint
if (value is ValueType)
{
ValueType structValue = (ValueType)value;
return value.Equals(DBNull.Value) ? default(T) : (T)structValue;
}
*/
return value == null || value.Equals(DBNull.Value) ? default(T) : (T)value;
}
The problem is that when the database returns an Interger, the value
variable will contain an int
, and when T
is a short
, I get an InvalidCastException
.
How can I improve this method to handle this situation? I want the method's user not to be concerned about this kind of problem, not having to double-cast. Is this possible?
I found a way using Convert.ChangeType(object, type)
(see the code comments for explanation):
public static T GetVerifiedValue<T>(this IDataRecord record, int index)
{
object value = record[index];
if (value == null || value.Equals(DBNull.Value))
return default(T);
//This handles nullable values, because sometimes there is a need for
//a casting on the underlying value before the final cast
Type nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(T));
if (nullableUnderlyingType != null)
{
if (nullableUnderlyingType.IsEnum)
return (T)Enum.ToObject(nullableUnderlyingType, value);
else
return (T)Convert.ChangeType(value, nullableUnderlyingType);
}
//Enums must be handled before the ValueTypes, becouse
//enums are also ValueTypes and using Convert.ChangeType with it will fail
if (typeof(T).IsEnum)
return (T)Enum.ToObject(typeof(T), value);
//######################################################################
//Here is the trick: as Convert.ChangeType returns an object,
//it is compatible with the unconstrained T. Worked nicely.
if (value is ValueType)
{
ValueType structValue = (ValueType)value;
return (T)Convert.ChangeType(structValue, typeof(T));
}
//######################################################################
if (value is string)
value = string.IsNullOrEmpty(value as string) ? null : value;
return (T)value;
}