在包含数值的字符串字段上使用 datareader.getString() 时出现 InvalidCastException

InvalidCastException when using datareader.getString() on a string field that contains numerical value

我在 sqlite 数据库中有一个字段,我们将其称为 field1,我试图在该字段上迭代每条记录(有超过一千条记录)。字段类型为 string。前四行field1的值如下:

DEPARTMENT
09:40:24
PARAM
350297

这是我用来遍历每一行并显示值的一些简单代码:

while (sqlite_datareader.Read())
{
     strVal = sqlite_datareader.GetString(0);
     Console.WriteLine(strVal);
}

前 3 个值显示正确。但是,当它到达数字条目 350297 时,它会出错,并在 .getString() 方法

上出现以下异常
An unhandled exception of type 'System.InvalidCastException' occurred in System.Data.SQLite.dll

我试过转换为字符串和其他一些东西。但我无法弄清楚为什么会这样。现在,我不得不使用 getValue,它是 object 类型,然后转换回字符串。但我想弄清楚为什么 getString() 在这里不起作用。

有什么想法吗?

编辑:这是我目前处理问题的方式:

 object objVal;  // This is declared before the loop starts...

 objVal = sqlite_datareader.IsDBNull(i) ? "" : sqlite_datareader.GetValue(i);
 if (objVal != "")
 {
     strVal = (string)objVal;
 } 

问题应该包括的是

  1. table 模式,最好是用于定义 table.
  2. 的 CREATE TABLE 语句
  3. 打开sqlite_datareader时使用的SQL语句。

任何时候处理数据库中的数据类型问题时,包含此类信息都是明智的做法。否则,当在模式 DDL 中明确定义非常有用的关键信息时,会有很多不必要的猜测和挣扎(如注释中所示)。获取数据的底层查询可能不太重要,但如果存在 CAST 语句 and/or 其他可能影响返回类型的表达式,它很可能成为问题的一部分。如果我在自己的系统上调试问题,我会首先检查这些!


评论包含很好的讨论,但最好的解决方案是直接从官方文档中理解 sqlite handles data types 的方式。关键要点是 sqlite 在列上定义类型亲和性,然后根据一组有限的存储 classes 存储实际值。类型亲和性是数据在存储之前将尝试转换成的类型。但是(来自文档)...

The important idea here is that the type is recommended, not required. Any column can still store any type of data.

但现在考虑...

A column with TEXT affinity stores all data using storage classes NULL, TEXT or BLOB. If numerical data is inserted into a column with TEXT affinity it is converted into text form before being stored.

所以即使任何存储class的值可以存储在任何列中,默认行为应该是在存储值之前将任何数值(如 350297)转换为字符串...如果列已正确声明为 TEXT 类型

但如果您足够仔细地阅读,您最终会在 3.1.1 部分的末尾看到以下内容。相似性名称示例

And the declared type of "STRING" has an affinity of NUMERIC, not TEXT.

因此,如果按字面意思理解问题的详细信息并且 field1 被定义为 field1 STRING,那么从技术上讲它具有 NUMERIC 亲和力并且 所以像 350297 这样的值将已存储为整数,而不是字符串。问题中描述的行为正是将数据检索到 System.Data.SQLite.

等严格类型数据模型时所期望的行为

很容易对这种不直观的设计决策进行咒骂,我不会为这种行为辩护,但是

  1. 至少 "STRING" 类型的结果被清楚地说明,以便可以将列重新定义为 TEXT 以解决问题,并且
  2. "STRING" 实际上不是标准的 SQL 数据类型。 SQL 字符串改为使用 TEXT、NTEXT、CHAR、NCHAR、VARCHAR、NVARCHAR 等定义。

解决方案要么使用当前实现的代码:将所有值作为对象获取,然后转换为字符串值...这应该普遍适用于 .Net 对象,因为他们都应该定义了 ToString() 方法。

或者,将列重新定义为具有类似

的 TEXT 亲和力
CREATE TABLE myTable (
   ...
   field1 TEXT,
   ...
)

究竟如何重新定义现有的充满数据的列完全是另一个问题。但是,至少在进行从原始列到新列的转换时,请记住使用 CAST(field1 AS TEXT) 以确保为现有数据更改存储 class。 (我不确定类型亲和性是否是 "enforced",当简单地 copying/inserting 数据从现有 table 到另一个,或者如果原始存储 class 默认保留。这就是为什么我建议强制转换为文本值。)