我正在尝试扩展DataRow扩展的Field方法,以添加一个参数来检查列是否存在:
public static T? FieldValue<T>(
this DataRow row,
string columnName,
bool checkColumn = false) where T : struct
{
return
checkColumn
&& !row.Table.Columns.Contains(
columnName)
? default(T)
: row.Field<T>(
columnName);
}
这适用于int,datetime等。但是,当我尝试将它与字符串一起使用时,它显示错误:
The type string must be non-nullable
如果数据库中存在空值,我也会收到错误:
Cannot cast DBNull.Value to type 'System.Decimal'
有没有办法可以无缝扩展Dataextension?
答案 0 :(得分:2)
正如马库斯指出的那样,你在这里遇到了两个挑战。一个是关于结构与值的类型,另一个是关于你必须处理空值的事实。
第二个问题很容易解决:只需在您的实施中添加?
,如下所示:row.Field<T?>(columnName)
您的例外将会消失。
根据您上面的代码,我假设您很高兴能够获得Nullable类型,即使对于非可空列也是如此。因此,您可以采取一些措施来支持您拥有的参考类型,并且仍然可以避免过多的代码重复:
// value type version
public static T? FieldValueStruct<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : struct
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T? /*with a question mark!!!*/ >);
}
// reference type version
public static T FieldValueClass<T>(this DataRow row, string columnName, bool checkColumn = false)
where T : class
{
return row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
// shared amongst value and reference type implementation
private static T GetValue<T>(this DataRow row, string columnName, bool checkColumn, T defaultValue, Func<string, T> getter)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? defaultValue
: getter(columnName);
}
使用此代码,您可以获得所需的功能,但需要付出代价:当您调用这些方法时,您需要指定类型参数(就像您现在一样),因为类型推断不会起作用({ {3}})。
string s;
// no type inference, type parameter must be specified
s = row.FieldValueClass<string>("test");
此外,您需要区分值类型版本和参考类型版本之间的调用,这些版本并不简单。为什么我们需要为这些方法使用两个不同的名称?原因是你不能通过简单地添加不同的类型约束来重载方法(参见here is why)。
类型推断主题可以通过使用out参数来解决,然而,这又带来了一系列缺点......
// value type version that works with type inference
public static void FieldValueStruct<T>(this DataRow row, string columnName, out T? value, bool checkColumn = false)
where T : struct
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T?>);
}
// reference type version that works with type inference
public static void FieldValueClass<T>(this DataRow row, string columnName, out T value, bool checkColumn = false)
where T : class
{
value = row.GetValue(columnName, checkColumn, default(T), row.Field<T>);
}
现在,您可以在没有类型参数的情况下调用您的方法:
string s;
// with type inference, doesn't work with properties, though, only with fields
row.FieldValueClass("test", out s);
不幸的是,这不适用于属性 - 仅适用于字段。
你看,世界是邪恶的,有时我们不能做太多。 ;)
根据您的评论进行更新:
下面的代码稍微改变了你的语义,但也许还可以:
public static T FieldValue<T>(this DataRow row, string columnName, bool checkColumn = false)
{
return checkColumn && !row.Table.Columns.Contains(columnName)
? default(T)
: row.Field<T>(columnName);
}
调用此方法需要看起来像:
// this will return 0 if the column is not available, a null value from the database will cause an exception
int i = r.FieldValue<int>("test");
// this will return null if the column is not available, a null value from the database would be ok
int? iNullable = r.FieldValue<int?>("test");
// this will return null if the column is not available, a null value from the database would be ok
string s = r.FieldValue<string>("test");
答案 1 :(得分:1)
第一条错误消息的原因是where
- 约束:
where T : struct
此约束要求用作类型参数的每个类型T都是值类型。 string
是引用类型,因此是错误消息。为了解决问题,如果您不需要,则应删除约束。
关于Null值问题,您应检查列是否为空(如果存在),在这种情况下也返回default(T)
。您可以使用DataRow.IsNull方法检查单元格是否为空。