DataRow.Field<T>(string Column) 抛出无效转换异常。

11

你好,

IDE Visual Studio 2010
.NET 3.5
平台 WinForms

这个SO问题“从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<object>("ID"),单元格值类型为short而不是int。为什么会发生这种情况,因为在表定义中,它明确地是int?此外,short应隐式转换为int,因为short更小,应该“适合”吧?


尝试使用 drSource.Field<int?>("ID"),然后让我知道结果。 - Vetrivel mp
你试过了吗?它能正常工作吗? - Pranay Rana
请看我的评论 - 实际上您的查询返回了 DbNull 值。因此,问题在于数据,而不是 Field<T> 扩展。 - Sergey Berezovskiy
大家都提供了有用的信息,谢谢。在这种情况下一个"非常强"的转换例如int iID = (int)drSource["ID"];或者int iID = Convert.ToInt32(drSource["ID"]);可以解决问题(因为int iID = drSource.Field<int>("ID");甚至不允许将short隐式转换为int),但是事先检查“是否可转换”也可能是个好主意。 - that0th3rGuy
实际上,如果字段类型是short,你需要使用Field<short>("ID")。顺便问一下,如果在数据库中(如您提供的),您有int而不是small int,为什么它是short? - Sergey Berezovskiy
是的,关于那个问题。我被我的同事误导了,没有自己进行调查,而是相信了他的调查。不过,这让我意识到了某些不规则的地方,现在我可以进行补偿。感谢您的时间。 - that0th3rGuy
5个回答

11
如果你的列是可空整数,但你正在尝试使用默认值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"); 
    }
}

如果您的列是可空整数且想要分配给可空整数:

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

9
同意Richard的回答,只是最好使用更好的方式。
int iID = Convert.ToInt32(drSource["ID"]);

(int)drSource["ID"] 对我没用。我不得不使用上面的答案。 - J King
这是唯一一个对我有效的转换,而其他所有的都没有。 - Andreas

5

出于好奇,如果您从键值集合中明确地进行类型转换会发生什么?

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

2
这个答案没有意义,因为 OP 的 Field<int>("ID") 做了同样的事情。 - Tim Schmelter
3
不,它正在工作,最好使用 int iID = Convert.ToInt32(drSource["ID"]); - Himalaya Garg

5

根据字段扩展的实现,您的字段具有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 数据库时遇到了这个异常,而在 SQL Server 上完全相同的数据/表定义却没有抛出异常。 - Andreas

2

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

int iID = Convert.ToInt32(row.Field<object>("ID"));


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接