Reputation: 100
I'm trying to write a generic method to read data from SQLDataReader
. It works really well except when I want to get custom default value for some data types. For example, for string
I want to get string.Empty
instead of null
.
public static T SafeGetValue<T>(SqlDataReader dr, string columnName)
{
T returnValue = default(T);
var value = dr[columnName];
if (value != null && value != DBNull.Value)
{
returnValue = (T)value;
}
else
{
returnValue.Null();
}
return returnValue;
}
public static object Null(this object o)
{
return null;
}
public static string Null(this string stringValue)
{
return string.Empty;
}
When T
is string
, I'm trying to get it to goto Null
overload of string
but it still goes to the object
overload. Is there any way to do this?
Upvotes: 2
Views: 1469
Reputation: 1088
For such case I'm using optional parameter in method with default value. So you override default behavior only where it's needed:
public static T GetValueOrDefault<T>(SqlDataReader dr, string columnName, T defaultValue = default(T))
{
T returnValue = default(T);
var value = dr[columnName];
return value == null ? (T) defaultValue : value;
}
If you want to go even deeper and handle cases when column doesn't exists or you have deals with Nullable types - here the part of code I'm using in my projects:
public static T GetValueOrDefault<T>(IDataRecord row, string fieldName, T defaultValue = default(T))
{
if (HasColumn(row, fieldName))
{
int ordinal = row.GetOrdinal(fieldName);
object value = row.GetValue(ordinal);
return row.IsDBNull(ordinal)
? defaultValue
: ConvertValue<T>(value);
}
else
{
return defaultValue;
}
}
public static bool HasColumn(IDataRecord row, string columnName)
{
for (var i = 0; i < row.FieldCount; i++)
{
if (row.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
public static T ConvertValue<T>(object value)
{
var type = typeof(T);
type = Nullable.GetUnderlyingType(type) ?? type;
var result = Convert.ChangeType(value, type);
return (T)result;
}
Upvotes: 0
Reputation: 119156
To expand on the comment I gave above, I suggested passing in a default value, but you can also pass in a generator instead, something you would only ever need to change in a single place. For example:
public class DefaultValueGenerator<T>
{
public virtual T Default()
{
return default(T);
}
}
public class StringValueGenerator : DefaultValueGenerator<string>
{
public override string Default()
{
return "";
}
}
public static T SafeGetValue<T>(SqlDataReader dr, string columnName,
DefaultValueGenerator<T> defaultGenerator)
{
//snip
returnValue = defaultGenerator.Default();
}
And use it like this:
var stringDefaultGenerator = new StringValueGenerator();
var x = SafeGetValue<string>(dr, "column", sg);
Upvotes: 1
Reputation: 2923
This smells like you're trying to have your data access layer cover up for data-not-present errorhandling in upper layers.
While that may seem like a good idea to avoid some errors globally, it might hurt you in the long run as you might not be able to distinguish between "record not present" and "record has the value I chose to cover up null" in the future. It also won't help you in cases where you'd like to supply a different default for the same type in a different usage scenario.
I just wanted to put this up so that you have a clear idea that there is also a price for taking this path.
Personally, I'd go for a T defaultValue
parameter, have some Option<T>
as a return type or possibly an out T
parameter and a bool
return type. This gives up neither compile-time safety, nor per-usage missing record handling.
Upvotes: 0
Reputation: 7360
The Null method is statically bound to the object version. I think your simplest option is to use a switch, or a dictionary, to handle your special cases. Like this:
private static readonly Dictionary<Type, Object> _NullValues = new Dictionary<Type, Object>()
{
{ typeof(String), String.Empty }
};
public static object Null<T>(this T o)
{
object ret;
return _NullValues.TryGetValue(typeof(T), out ret)
? ret : default(T);
}
Upvotes: 4