列中的 NULL 值出现 Dapper Cast 异常
Dapper Cast Exception on NULL value in column
我正在使用 SQLite 及其闭包扩展来存储层次结构。非闭包 table 创建为
_connection.Execute(@"CREATE TABLE IF NOT EXISTS category (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT,
parent_id INTEGER,
FOREIGN KEY (parent_id) REFERENCES category (id)
);");
插入根节点时 parent_id
设置为 NULL
。 Dapper 来回转换的 class 是
public class TestRecord
{
public long id;
public string name;
public long? parent_id;
}
在我看来,Dapper 读取根节点或非根节点应该没有任何问题,因为相关列已明确标记为可为空。但是,像这样查询所有条目:
_connection.Query<TestRecord>(@"SELECT * FROM category;");
将在根节点上抛出,因为它不能抛出某些东西(这很奇怪,因为任何地方都没有涉及 32 位整数):
Unhandled Exception: System.Data.DataException:
Error parsing column 2 (parent_id=1 - Int64) --->
System.InvalidCastException: Unable to cast object of type 'System.Int64' to type
'System.Int32'.
解决方法 returns 正确的结果是
.Query<TestRecord>(@"SELECT id, IFNULL(parent_id, 0), name FROM category;");
但出于多种原因这是不行的。我特别不想列出查询中的所有列,也不想引入 parent_id
特例。
不使用 Dapper 并手动映射它可以很好地处理原始查询,当然 sqlite CLI 也是如此。
那么,我怎样才能让 Dapper 接受并映射正确的条目?
编辑:我正在使用 Dapper 1.50.4 和 dotnet core 2.0。
如果你用 "INTEGER" 创建你的 table,Sqlite 将创建 int32,但是你的模型中有一个 long,我想这是抛出了无效的转换异常,BigInt 必须是曾经给个long。
所以您应该将请求更改为:
_connection.Execute(@"CREATE TABLE IF NOT EXISTS category (
id BIGINT NOT NULL PRIMARY KEY,
name TEXT,
parent_id BIGINT,
FOREIGN KEY (parent_id) REFERENCES category (id)
);");
或使用符合您要求的型号:
public class TestRecord
{
public int id;
public string name;
public int? parent_id;
}
我猜你对 "IFNULL(parent_id, 0)" 的请求是有效的,因为它包含一个转换,因为 0 可以被认为是 int32。
每个 this should not happen and it warrents a library fix. The issue is being tracked here 并且还会影响其他人。
sqlite 可空类型的解决方案。 link
public class NullableLongHandler : SqlMapper.TypeHandler<long?>
{
public override void SetValue(IDbDataParameter parameter, long? value)
{
if (value.HasValue)
parameter.Value = value.Value;
else
parameter.Value = DBNull.Value;
}
public override long? Parse(object value)
{
if (value == null || value is DBNull) return null;
return Convert.ToInt64(value);
}
}
和
SqlMapper.AddTypeHandler(new NullableLongHandler());
我正在使用 SQLite 及其闭包扩展来存储层次结构。非闭包 table 创建为
_connection.Execute(@"CREATE TABLE IF NOT EXISTS category (
id INTEGER NOT NULL PRIMARY KEY,
name TEXT,
parent_id INTEGER,
FOREIGN KEY (parent_id) REFERENCES category (id)
);");
插入根节点时 parent_id
设置为 NULL
。 Dapper 来回转换的 class 是
public class TestRecord
{
public long id;
public string name;
public long? parent_id;
}
在我看来,Dapper 读取根节点或非根节点应该没有任何问题,因为相关列已明确标记为可为空。但是,像这样查询所有条目:
_connection.Query<TestRecord>(@"SELECT * FROM category;");
将在根节点上抛出,因为它不能抛出某些东西(这很奇怪,因为任何地方都没有涉及 32 位整数):
Unhandled Exception: System.Data.DataException:
Error parsing column 2 (parent_id=1 - Int64) --->
System.InvalidCastException: Unable to cast object of type 'System.Int64' to type
'System.Int32'.
解决方法 returns 正确的结果是
.Query<TestRecord>(@"SELECT id, IFNULL(parent_id, 0), name FROM category;");
但出于多种原因这是不行的。我特别不想列出查询中的所有列,也不想引入 parent_id
特例。
不使用 Dapper 并手动映射它可以很好地处理原始查询,当然 sqlite CLI 也是如此。
那么,我怎样才能让 Dapper 接受并映射正确的条目?
编辑:我正在使用 Dapper 1.50.4 和 dotnet core 2.0。
如果你用 "INTEGER" 创建你的 table,Sqlite 将创建 int32,但是你的模型中有一个 long,我想这是抛出了无效的转换异常,BigInt 必须是曾经给个long。
所以您应该将请求更改为:
_connection.Execute(@"CREATE TABLE IF NOT EXISTS category (
id BIGINT NOT NULL PRIMARY KEY,
name TEXT,
parent_id BIGINT,
FOREIGN KEY (parent_id) REFERENCES category (id)
);");
或使用符合您要求的型号:
public class TestRecord
{
public int id;
public string name;
public int? parent_id;
}
我猜你对 "IFNULL(parent_id, 0)" 的请求是有效的,因为它包含一个转换,因为 0 可以被认为是 int32。
每个
sqlite 可空类型的解决方案。 link
public class NullableLongHandler : SqlMapper.TypeHandler<long?>
{
public override void SetValue(IDbDataParameter parameter, long? value)
{
if (value.HasValue)
parameter.Value = value.Value;
else
parameter.Value = DBNull.Value;
}
public override long? Parse(object value)
{
if (value == null || value is DBNull) return null;
return Convert.ToInt64(value);
}
}
和
SqlMapper.AddTypeHandler(new NullableLongHandler());