Generic method to read data from DataReader

☆樱花仙子☆ 提交于 2019-12-05 04:33:26
nawfal

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

Starting with .NET Framework 4.5

static class SqlReaderExtension
{
    public static async Task<T> ReadAsync<T>(this SqlDataReader reader, string fieldName)
    {
        if (reader == null) throw new ArgumentNullException(nameof(reader));
        if (string.IsNullOrEmpty(fieldName))
            throw new ArgumentException("Value cannot be null or empty.", nameof(fieldName));

        int idx = reader.GetOrdinal(fieldName);
        return await reader.GetFieldValueAsync<T>(idx);
    }
}

and then

string result = await reader.ReadAsync<string>("FieldName");
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!