将 DataRow 值转换为强类型值
Posted
技术标签:
【中文标题】将 DataRow 值转换为强类型值【英文标题】:Convert DataRow value to strongly typed value 【发布时间】:2015-11-10 14:41:41 【问题描述】:我想将 DataRow 中的值转换为强类型变量。我想到了类似的东西:
int id = row.Field<int>("ID");
但问题是,我们的标准 Db-Select 返回一个 DataTable 其中每个值都是字符串类型。意味着每个值都是对象,列没有特定类型。通过这个Field<T>
抛出 InvalidCastException 因为我尝试将对象转换为 int。
所以我自己做了一些事情:
public static T ConvertTo<T>(this DataRow row, string columnName)
T returnValue = default(T);
Type typ = Nullable.GetUnderlyingType(typeof(T));
if ((row != null) &&
(row[columnName] != null) &&
(!Convert.IsDBNull(row[columnName])))
if (typ == null)
returnValue = (T)Convert.ChangeType(row[columnName], typeof(T));
else
returnValue = (T)Convert.ChangeType(row[columnName], typ);
return returnValue;
此方法按预期工作。我可以转换可为空的值、字符串和值类型。好的,如果有人输入像自己的类型这样的废话,我需要捕获异常。但其他都很好。
但是,如果我加载一个包含 30000 行和 20 列的 DataTable,并希望使用此方法来转换每个值以创建对象,我会遇到巨大的性能缺陷。如果我对每个值使用int id = Convert.ToInt32(row["ID"]);
等等,它比我的一般方法快 5-6 倍。但我不喜欢手动转换它们,特别是如果有很多DBNull.Value
。
我认为问题在于通过以下方式获取基础类型:
Type typ = Nullable.GetUnderlyingType(typeof(T));
这是反射调用并减慢我的方法。这是正确的吗?有没有人遇到同样的问题并且有一个快速的解决方案?
更新:
我们的标准选择是这样工作的:
DbDataAdapter dataAdapter = new DbDataAdapter();
dataAdapter.SelectCommand = cmd;
dataSet = new DataSet();
dataAdapter.Fill(dataSet);
dtReturn = dataSet.Tables[0].Copy();
DbDataAdapter 内部使用 OracleDataAdapter。
更新:
为了澄清,我正在做以下事情(只是一个小例子):
我有一个代表选择查询的类。例如:
public class Customer
public int Idget;set
public string Nameget;set
在数据库 (Oracle) 中,我有一个“客户”表,其中有 2 列 Id number(5)
和 Name varchar2(100)
。
现在我想阅读所有客户并将它们转换为客户对象。 所以我通过我们的标准例程 SqlSelect 读取数据。这返回给我一个数据表。这个方法的实习生是在第一次UPDATE之前贴出来的。
现在我遍历此 DataTable 中的每个 DataRow 并转换此单元格值以创建对象。
List<Customer> myList = new List<Customer>();
foreach (DataRow row in SqlSelect())
Customer customer = new Customer();
customer.Id = Convert.ToInt32(row["ID"]);
customer.Name = Convert.ToString(row["NAME"]);
myList.Add(customer);
这样就好了。但我想像这样转换:
customer.Id = row.ConvertTo<int>("ID");
如果我在 row["ID"] 处的 DataTable 是 int 字段方法可以很好地处理这个问题。特别是如果还有其他列可以为空等。但在我的情况下,行[“ID”](以及每个选择的所有其他值)是字符串。所以菲尔德不能施放这个。我的 ConvertTo 可以做到,但在大型表上性能欠佳。
【问题讨论】:
使用分析器。为什么我们会知道您的 CPU 将时间花在哪里?此外,使用Convert
也有没有的意义。该值要么是DBNull.Value
,要么是T
类型——只需进行简单的转换即可。
您的“标准数据库选择返回无类型数据表”?这怎么可能?你怎么填那张桌子?通常DataAdapter
从数据读取器中检索模式信息。 “我不喜欢手动转换它们,特别是如果有很多 DBNull.Value
”。 DBNull.Value
有什么问题? DataRow.Field
支持可为空的类型。
@TimSchmelter 如果我可以使用 FieldDataSet
用于其他目的,您可以return dataSet.Tables[0]
@TimSchmelter 感谢您的提示。我没有开发这个程序。这是我们的标准,由一位同事制定。我会问他...
【参考方案1】:
你在这里做的很奇怪。您必须了解,C# 中的所有数据类型都继承自对象。所以int、int?、char、double、class MyClass、struct MyStruct,都是从object继承而来的。
所以 row[columnName] 包含 int 类型的数据,那么即使是返回对象,也可以直接转换为 int。
int i = (int)row[columnName]; //when int data,
这里的例外是,当数据为 DBNUll 时,必须在转换前进行测试。 确实不需要 Convert 类,因为它正在大量使用反射,这是性能损失的根源!
编辑[1]:@Scott Chamberlain 是对的,我改进了代码以使其对值类型更安全:
public static class IsNullable<T>
private static readonly Type type = typeof(T);
private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
public static bool Result get return is_nullable;
public static class Extension
public static T CastColumnData<T>(this DataRow row,
string columnName)
object obj;
if (row == null) throw new ArgumentNullException("row is null");
if ((obj = row[columnName]) == null) throw new ArgumentNullException("row[" + columnName + "] is null");
bool is_dbnull = obj == DBNull.Value;
if (is_dbnull && !IsNullable<T>.Result) throw new InvalidCastException("Columns data are DbNull, but the T[" + typeof(T).ToString() + "] is non nullable value type");
return is_dbnull ? default(T) : (T)obj;
Class Convert 用于其他目的,用于在两种数据类型之间创建等效值,它们共享一些共同点,但在这里它是使用大量反射的相当繁重的东西。
object 和 int 不等价,object 是由 int 继承的,即使不是直接...
编辑[2]:对原始问题的新更新最终澄清,所有列类型实际上都是字符串,而不是不同类型的数据类型,它们必须被转换/解析为请求的数据类型。强>
恐怕没有更快的解决方案,因为值类型不会从具有泛型类型的接口实现 Parse 和 TryParse。它们是静态方法,这使得通过泛型解决这个问题的尝试非常有问题。
【讨论】:
对于 int 之类的返回default(T)
,我总是很谨慎,我不喜欢没有办法区分 0 和 DbNull。如果该类不可为空,则抛出异常通常是更安全的选择。您的代码适用于可空类型(Nullable<T>
结构和类),只是不可为空的类型我不信任它。
@ipavlu 很抱歉,存储在 DataTable 中的值不是对象。它们都是字符串。我在我的问题中纠正了这一点。 Fieldvarchar
,或者您省略了有人使用row["column].ToString()
而不是使用row.Field<int>("column")
的代码。【参考方案2】:
这是你想要的吗?如果错误经常发生,就会有人无意中听到它们,但如果不是,我认为这不是问题。 Convert.ChangeType 可能在场景下使用反射,所以可能也有点慢......
public static T ConvertTo<T>( this DataRow dataRow, string columnName )
var defaultValue = default( T );
var valueOfCell = GetCellValue(dataRow, columnName);
if ( defaultValue == null && valueOfCell == null )
return default( T );
try
return ( T )Convert.ChangeType( valueOfCell, typeof( T ) );
catch ( InvalidCastException ex )
return default( T );
【讨论】:
以上是关于将 DataRow 值转换为强类型值的主要内容,如果未能解决你的问题,请参考以下文章
如何在不使用 foreach 的情况下将 ArrayList 转换为强类型泛型列表?
获取DataGridView上选中的一行并转换为一个DataRow类型
如何使用 StreamBuilder 消除颤振数据表错误“预期类型为 'List<DataRow>',但得到类型为 'List<dynamic>”的值之一?