Use Generic class with SqlDataReader GetValue

1.4k views Asked by At

I have this code that works ok with a database with no NULL values

public T GetField<T>(SqlDataReader dr, string fieldName)
{
    return (T)dr.GetValue(dr.GetOrdinal(fieldName));
}

Then I want to control the DBNull values, because they already exist in some tables and I upgraded the function like this:

public T GetField<T>(SqlDataReader dr, string fieldName) where T : new()
{
    var value = dr.GetValue(dr.GetOrdinal(fieldName));   
    return value is DBNull ? new T() : (T)value;
}

But now, I cannot ask for GetField<string> because string has no constructor with 0 parameters.

I have tried to create another function like the Abstract one, with no restrictions and only with string type like this

public string GetField<string>(SqlDataReader dr, string fieldName)
{
    var value = dr.GetValue(dr.GetOrdinal(fieldName));   
    return value is DBNull ? "" : value.ToString();
}

but the answer is that they are ambiguous definitions, because T also includes string, even with the restriction applied.

I could create independent functions for each datatype, but I rather prefer the abstract solution because it is way much cleaner.

Did I miss something?

Thanks!

2

There are 2 answers

1
Jeroen Mostert On BEST ANSWER

Here's one approach:

public T GetField<T>(SqlDataReader dr, string fieldName)
{
    var value = dr.GetValue(dr.GetOrdinal(fieldName));
    return value is DBNull ? default(T) : (T) value;
}

This has the added benefit of working with nullable types. Then you'd call that as:

string x = foo.GetField<string>(reader, "field") ?? "";

Which is deliberately contrived in this case -- Jon's version with a default suits you better if usually you want something other than null returned.

2
Jon Skeet On

Your final method would fail because it's trying to declare a type parameter called string.

I suggest you add another overload without the constraint on T, but with a default value:

public T GetField<T>(SqlDataReader dr, string fieldName, T defaultValue)
{
    var value = dr.GetValue(dr.GetOrdinal(fieldName));   
    return value is DBNull ? defaultValue : (T) value;
}

Then you'd call it as:

string x = foo.GetField(reader, "field", "");

... with the added benefit that the method isn't string-specific.