【问题标题】:DataRow.Field<T>(string Column) throws invalid cast exceptionDataRow.Field<T>(string Column) 抛出无效的强制转换异常
【发布时间】:2012-03-27 08:10:15
【问题描述】:

早安,

IDE Visual Studio 2010
.NET 3.5
平台 WinForms

SO 问题“difference between getting value from DataRow”指代。

我有一个包含[ID] INT IDENTITY(1, 1) NOT NULL PRIMARY KEY 列的数据库表。查询此表并将值存储在局部变量中时,我得到一个无效的强制转换异常;示例代码:

string sQuery = @"
    SELECT [ID], [Description]
    FROM [Sources]
    ORDER BY [Description] ";

using (DataTable dtSources = SQLHelper.Fetch(sQuery))
{
    foreach (DataRow drSource in dtSources.Rows)
    {
        int iID = drSource.Field<int>("ID"); // InvalidCastException
        string sDescrption = drSource.Field<string>("Description");
    }
}

当单步执行并在故障行上执行“快速监视”时,我发现通过将行更改为drSource.Field&lt;object&gt;("ID"),单元格值类型是short 而不是int。为什么会发生这种情况,在表定义中,这显然是 int?此外,short 应该隐式转换为int,因为short 更小并且应该“适合”对吗?

【问题讨论】:

  • drSource.Field("ID")。试试这个,让我知道。
  • 你试过了吗?有效吗?
  • 请参阅下面的评论 - 您实际上有查询返回的 DbNull 值。所以,数据问题,而不是 Field 扩展。
  • 大家提供了有用的信息,谢谢。在这种情况下,诸如int iID = (int)drSource["ID"]; 或int iID = Convert.ToInt32(drSource["ID"]); 之类的“不那么强”转换可以解决问题(因为int iID = drSource.Field&lt;int&gt;("ID"); 甚至不允许将short 隐式转换为int),但事先检查“可转换性”会可能也是个好主意。
  • 其实如果字段类型很短,需要使用Field("ID")。顺便说一句,为什么它很短,如果在数据库中(如你提供的那样)你有 int,而不是小的 int。

标签: c# datarow


【解决方案1】:

如果您的列是可为空的 int,但您尝试分配给 int,默认值为 0:

using (DataTable dtSources = SQLHelper.Fetch(sQuery))
{ 
    foreach (DataRow drSource in dtSources.Rows)'
    { 
        int iID = drSource.Field<int?>("ID") ?? 0; 
        string sDescrption = drSource.Field<string>("Description"); 
    }
}

如果您的列是可为空的 int,并且您想分配给可为空的 int:

using (DataTable dtSources = SQLHelper.Fetch(sQuery))
{
    foreach (DataRow drSource in dtSources.Rows)
    {
        int? iID = drSource.Field<int?>("ID");
        string sDescrption = drSource.Field<string>("Description"); 
    }
}

【讨论】:

    【解决方案2】:

    同意理查德的回答只是更好用,

    int iID = Convert.ToInt32(drSource["ID"]);
    

    【讨论】:

    • (int)drSource["ID"] 对我不起作用。我不得不使用上面的答案
    • 这是唯一对我有用的转换,而其他所有转换都没有。
    【解决方案3】:

    出于好奇,如果您自己从键/值集合中显式地转换它会发生什么?

    int iID = (int)drSource["ID"];
    

    【讨论】:

    • 这个答案没有意义,因为 OP 的 Field&lt;int&gt;("ID") 也是如此。
    • 不工作,最好使用 int iID = Convert.ToInt32(drSource["ID"]);
    【解决方案4】:

    根据字段扩展的实现,您的字段具有 DbNull 值。

    public static T Field<T>(this DataRow row, string columnName)
        {
            DataSetUtil.CheckArgumentNull<DataRow>(row, "row");
            return UnboxT<T>.Unbox(row[columnName]);
        }
    

    UnboxT 是一个私有类,它提供将对象转换为 T 的方法。在您的情况下使用 ValueField 转换器:

    private static class UnboxT<T>
    {
        internal static readonly Converter<object, T> Unbox;
    
        static UnboxT()
        {
           DataRowExtensions.UnboxT<T>.Unbox =  
              new Converter<object, T>(DataRowExtensions.UnboxT<T>.ValueField);
        }
    
        private static T ValueField(object value)
        {
            if (DBNull.Value == value)
            {
                // You get this exception 
                throw DataSetUtil.InvalidCast(Strings.DataSetLinq_NonNullableCast(typeof(T).ToString()));
            }
            return (T) value;
        }
    }
    

    【讨论】:

    • 这真的很有趣,首先因为我使用 Oracle DB 得到了这个异常,而完全相同的数据/表定义不会在 SQL Server 上引发异常。
    【解决方案5】:

    将行值转换为 int 变量的另一种方法是使用 'object' 并将其转换为 Int32,如下所示:

    int iID = Convert.ToInt32(row.Field&lt;object&gt;("ID"));

    【讨论】:

      猜你喜欢
      • 2015-01-27
      • 1970-01-01
      • 1970-01-01
      • 2012-02-20
      • 1970-01-01
      • 2012-09-21
      • 1970-01-01
      • 1970-01-01
      • 2012-05-28
      相关资源
      最近更新 更多