我正在尝试编写一个通用方法来从SQLDataReader
读取数据。它工作得很好,除非我想为某些数据类型获取自定义默认值。例如,对于string
,我想得到string.Empty
而不是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;
}
当T
string
时,我试图让它进入Null
string
过载,但它仍然进入object
过载。有什么办法可以做到这一点吗?
Null 方法静态绑定到对象版本。我认为您最简单的选择是使用开关或字典来处理您的特殊情况。喜欢这个:
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);
}
为了扩展我上面给出的评论,我建议传入默认值,但您也可以传入生成器,您只需要在单个位置进行更改。例如:
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();
}
并像这样使用它:
var stringDefaultGenerator = new StringValueGenerator();
var x = SafeGetValue<string>(dr, "column", sg);
听起来像是你试图让数据访问层掩盖上层中不存在的数据错误处理。
虽然这似乎是避免全局错误的一个好主意,但从长远来看,它可能会伤害您,因为您将来可能无法区分"记录不存在"和"记录具有我选择掩盖 null 的值"。如果您希望在不同的使用方案中为同一类型提供不同的默认值,它也不会帮助您。
我只是想把这个提出来,以便你清楚地知道走这条路也是有代价的。
就个人而言,我会选择一个T defaultValue
参数,有一些Option<T>
作为返回类型,或者可能是一个out T
参数和一个bool
返回类型。这既不会放弃编译时的安全性,也不会放弃每次使用丢失的记录处理。
我在方法中使用具有默认值的可选参数。因此,您仅在需要的地方覆盖默认行为:
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;
}
如果您想更深入地处理列不存在或处理 Nullable 类型的情况 - 这是我在项目中使用的代码部分:
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;
}