I am currently using this method to read data from DataReader -
private T GetValue<T>(object obj)
{
if (typeof(DBNull) != obj.GetType())
{
return (T)obj;
}
return default(T);
}
calling above method as -
GetValue<int>(dataReader["columnName1"])
GetValue<string>(dataReader["columnName2"])
GetValue<float>(dataReader["columnName3"])
However this fails when columnName3
is having values as 7200000
with error
Invalid Cast Exception.
I am thinking to modify my method to replace -
return (T)obj;
with
return (T)Convert.ChangeType(obj, typeof(T));
But looking forward for a better way as this change will involve type casting two times.
Any better ideas?
Thank you!
A generic method has the advantage that you can reduce a lot of code bloat, but otherwise churning out your own wrapper for each data type gives you the flexibility to have custom handling. And most probably your db queries will have a noticeable effect on performance than mode of retrieval.
I suggest you to write a set of your own extension methods than having one generic approach. Extending the method on IDataReader
gives you the benefit of not propagating the methods on entire object sub types. I have had to individually handle types since various connectors behaved differently especially with Guid
type. Also its hard to know if datareader read the value 0
or DBNull
when you're returning 0
for both cases. Lets say there is an enum field in your table with a null value. why would you ever want it to be read as the first enum?
Just call:
dataReader.GetInt("columnName1")
dataReader.GetString("columnName3")
dataReader.GetFloat("columnName3")
And the methods:
public static int? GetInt(this IDataReader r, string columnName)
{
var i = r[columnName];
if (i.IsNull())
return null; //or your preferred value
return (int)i;
}
public static bool IsNull<T>(this T obj) where T : class
{
return (object)obj == null || obj == DBNull.Value;
}
And similarly,
public static string GetString(this IDataReader r, string columnName)
{
}
public static float GetFloat(this IDataReader r, string columnName)
{
}
If you really want one generic function you can have it too.
public static T Get<T>(this IDataReader r, string columnName, T defaultValue = default(T))
{
var obj = r[columnName];
if (obj.IsNull())
return defaultValue;
return (T)obj;
}
So call it
dataReader.Get<int>(1); //if DBNull should be treated as 0
dataReader.Get<int?>(1); //if DBNull should be treated as null
dataReader.Get<int>(1, -1); //if DBNull should be treated as a custom value, say -1
That said, the error is because you're not casting with right type as pointed out in comments. I could have gone with built in DBNull
checks, but I don't to avoid data being read multiple times from the reader, inspired from this curious case of microoptimization