如果使用 UNION ALL,则 MySqlDataReader 为同一列返回不同的类型
本文关键字:一列 返回 类型 如果 ALL UNION MySqlDataReader | 更新日期: 2023-09-27 18:36:25
我有一个循环,通过从MySqlCommand生成的DataReader,其中查询从一些字段中选择,包括映射到C#上bool
的2个TINYINT(1)
字段,这就是我所期望的。
当我更改查询以对同一表进行UNION ALL
时,问题出现了。更改查询后,我开始收到无效的转换错误。TINYINT(1)
列现在返回 SByte
s 而不是 Boolean
。
这是 MySql 服务器的问题吗?MySql Net/Connector 问题?这是预期的行为吗?
示例查询:
string sql = @"SELECT tinyint1column FROM mytable WHERE id = 1";
command.CommandText = sql;
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
bool flag = (bool)reader["tinyint1column"]; // OK - No error
}
}
sql = @"SELECT tinyint1column FROM mytable WHERE id = 1
UNION ALL
SELECT tinyint1column FROM mytable WHERE id = 2";
command.CommandText = sql;
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
bool flag = (bool)reader["tinyint1column"]; // Invalid cast error???
}
}
在有人问之前:
- 我在共享主机上安装了MySql Server 5.1,因此无法升级服务器。
- 我使用的是 MySql.Data 版本 6.5.4,因为它是我唯一可以编译以在此共享主机上以中等/部分推力运行的版本。
- 我实际上是使用
.Cast<>()
扩展方法将读者"投射"到List<DbDataRecord>
以获得"断开的读取器",但它不会以任何方式更改基础数据。
我也可以使用 NET 连接器 6.3 确认问题(?)。
但是,有一个简单的方法可以调用reader.GetBoolean()
。
MySql 连接器中基本 IDbDataReader 的这种覆盖在读取器字段内部调用 Convert.ToBoolean()
public bool GetBoolean(string name)
{
return this.GetBoolean(this.GetOrdinal(name));
}
public override bool GetBoolean(int i)
{
return Convert.ToBoolean(this.GetValue(i));
}
因此,您可以轻松地使代码适应这种情况(它也适用于查询的单表版本)
using(var reader = command.ExecuteReader())
{
while(reader.Read())
{
bool flag = reader.GetBoolean("tinyint1column");
....
}
}
编辑 鉴于您在下面的评论,我认为您可以使用 DbDataRecord 类的扩展方法解决缺少 GetBoolean(字段名称)的问题。
我已经用LinqPad对此进行了测试,它似乎可以正常工作(如果字段为空,则返回的内容的一部分)
public bool GetBoolean(DbDataRecord rec, string fieldName)
{
int pos = rec.GetOrdinal(fieldName);
if(rec.IsDBNull(pos))
return false; // ??
object result = rec.GetValue(pos);
return Convert.ToBoolean(result);
}
我不确定这是 MySql 问题还是连接器问题,但这是一个问题。
在从@Steve的回答中获得一些灵感后,我提出了一个解决方案:
public static class DbDataRecordExtensions
{
public static bool GetBoolean(this DbDataRecord rec, string fieldName)
{
var index = rec.GetOrdinal(fieldName);
var value = rec.GetValue(index);
if (value is bool || value is Boolean)
{
return (bool)value;
}
else if (value is SByte || value is sbyte)
{
return (sbyte)value != 0;
}
else
{
return rec.GetInt64(index) != 0;
}
}
}
我无法进行通用强制转换,因为它会在联合返回行上的两个查询时更改类型,因此我编写了一些条件来克服此问题。
请注意,对于MySqlDataReader
,您必须更改扩展方法签名和方法名称:
GetBooleanEx(this MySqlDataReader rec, string fieldName)