将 DataRow 值转换为强类型值
Convert DataRow value to strongly typed value
我想将值从 DataRow 转换为强类型变量。我想到了类似的东西:
int id = row.Field<int>("ID");
但问题是,我们的标准 Db-Select returns 数据 Table 其中每个值都是字符串类型 。意味着每个值都是那里的对象,并且列没有特定类型。通过这个 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;
}
此方法按预期工作。我可以转换可为 null 的值、字符串和值类型。好的,如果有人像自己的类型一样输入废话,我需要捕获异常。不过其他都还好。
但是,如果我加载一个包含 30000 行和 20 列的数据Table,并且想使用此方法转换每个值以创建对象,我会遇到巨大的性能不足。如果我对每个值使用 int id = Convert.ToInt32(row["ID"]);
等等,它比我的一般方法快 5-6 倍。但我不喜欢手动转换它们,特别是如果有很多 DBNull.Value
。
我认为问题在于通过以下方式获取基础类型:
Type typ = Nullable.GetUnderlyingType(typeof(T));
这是反射调用并减慢了我的方法。这是正确的吗?有没有人遇到同样的问题并且有快速的解决方案?
更新:
我们的标准-Select是这样工作的:
DbDataAdapter dataAdapter = new DbDataAdapter();
dataAdapter.SelectCommand = cmd;
dataSet = new DataSet();
dataAdapter.Fill(dataSet);
dtReturn = dataSet.Tables[0].Copy();
DbDataAdapter 内部使用 OracleDataAdapter。
更新:
为了澄清,我正在做以下事情(只是一个小例子):
我有一个 class 代表一个 select 查询。例如:
public class Customer
{
public int Id{get;set}
public string Name{get;set}
}
在数据库 (Oracle) 中我有一个 Table "Customer" 有 2 列 Id number(5)
和 Name varchar2(100)
.
现在我想读取所有客户并将它们转换为客户对象。
所以我通过我们的标准例程 SqlSelect 读取数据。这个returns我一个数据Table。这个方法的实习生之前在第一次UPDATE下发过。
现在我遍历此数据中的每个数据行Table并转换此单元格值以创建对象。
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");
如果我在行["ID"] 的数据Table 是整数,则字段方法可以很好地处理这个问题。特别是如果还有其他列可以为 null 等。但在我的例子中,行 ["ID"](以及每个 select 的所有其他值)是字符串。所以菲尔德不能投这个。我的 ConvertTo 做到了,但在大型表上性能欠佳。
你在这里做的事情很奇怪。您必须了解,C# 中的所有数据类型都继承自对象。所以int, int?, char, double, class MyClass, struct MyStruct, 他们都是继承自object.
所以row[columnName]中包含的是int类型的数据,那么即使返回的是object,也可以直接转为int。
int i = (int)row[columnName]; //when int data,
这里的例外是,当数据为 DBNUl 时,必须在转换前进行测试。 确实不需要转换 class,因为它大量使用反射,这是性能损失的根源!
编辑[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]:对原始问题的新更新终于澄清了,所有列类型实际上都是字符串,而不是不同类型的数据类型,它们必须是 transformed/parsed请求的数据类型。
恐怕没有更快的解决方案,因为值类型不从具有泛型类型的接口实现 Parse 和 TryParse。它们是静态方法,这使得通过泛型解决这个问题的尝试变得非常有问题。
这是否符合您的要求?如果错误很频繁,就会有人无意中发现它们,但如果不是这样,我认为这不是问题。 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 转换为强类型变量。我想到了类似的东西:
int id = row.Field<int>("ID");
但问题是,我们的标准 Db-Select returns 数据 Table 其中每个值都是字符串类型 。意味着每个值都是那里的对象,并且列没有特定类型。通过这个 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;
}
此方法按预期工作。我可以转换可为 null 的值、字符串和值类型。好的,如果有人像自己的类型一样输入废话,我需要捕获异常。不过其他都还好。
但是,如果我加载一个包含 30000 行和 20 列的数据Table,并且想使用此方法转换每个值以创建对象,我会遇到巨大的性能不足。如果我对每个值使用 int id = Convert.ToInt32(row["ID"]);
等等,它比我的一般方法快 5-6 倍。但我不喜欢手动转换它们,特别是如果有很多 DBNull.Value
。
我认为问题在于通过以下方式获取基础类型:
Type typ = Nullable.GetUnderlyingType(typeof(T));
这是反射调用并减慢了我的方法。这是正确的吗?有没有人遇到同样的问题并且有快速的解决方案?
更新:
我们的标准-Select是这样工作的:
DbDataAdapter dataAdapter = new DbDataAdapter();
dataAdapter.SelectCommand = cmd;
dataSet = new DataSet();
dataAdapter.Fill(dataSet);
dtReturn = dataSet.Tables[0].Copy();
DbDataAdapter 内部使用 OracleDataAdapter。
更新:
为了澄清,我正在做以下事情(只是一个小例子):
我有一个 class 代表一个 select 查询。例如:
public class Customer
{
public int Id{get;set}
public string Name{get;set}
}
在数据库 (Oracle) 中我有一个 Table "Customer" 有 2 列 Id number(5)
和 Name varchar2(100)
.
现在我想读取所有客户并将它们转换为客户对象。 所以我通过我们的标准例程 SqlSelect 读取数据。这个returns我一个数据Table。这个方法的实习生之前在第一次UPDATE下发过。
现在我遍历此数据中的每个数据行Table并转换此单元格值以创建对象。
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");
如果我在行["ID"] 的数据Table 是整数,则字段方法可以很好地处理这个问题。特别是如果还有其他列可以为 null 等。但在我的例子中,行 ["ID"](以及每个 select 的所有其他值)是字符串。所以菲尔德不能投这个。我的 ConvertTo 做到了,但在大型表上性能欠佳。
你在这里做的事情很奇怪。您必须了解,C# 中的所有数据类型都继承自对象。所以int, int?, char, double, class MyClass, struct MyStruct, 他们都是继承自object.
所以row[columnName]中包含的是int类型的数据,那么即使返回的是object,也可以直接转为int。
int i = (int)row[columnName]; //when int data,
这里的例外是,当数据为 DBNUl 时,必须在转换前进行测试。 确实不需要转换 class,因为它大量使用反射,这是性能损失的根源!
编辑[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]:对原始问题的新更新终于澄清了,所有列类型实际上都是字符串,而不是不同类型的数据类型,它们必须是 transformed/parsed请求的数据类型。
恐怕没有更快的解决方案,因为值类型不从具有泛型类型的接口实现 Parse 和 TryParse。它们是静态方法,这使得通过泛型解决这个问题的尝试变得非常有问题。
这是否符合您的要求?如果错误很频繁,就会有人无意中发现它们,但如果不是这样,我认为这不是问题。 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 );
}
}