如果使用UNION ALL,则MySqlDataReader为同一列返回不同的类型

时间:2015-01-06 22:26:08

标签: c# mysql

我有一个循环通过从MySqlCommand生成的DataReader,它的查询从某些字段中选择,包括在C#上映射到TINYINT(1)的2个bool字段,这就是我所期待的是。

当我将查询更改为使用相同的表格执行UNION ALL时出现了问题。 在我更改了查询后,我开始收到无效的转换错误。 TINYINT(1)列现在返回SByte而不是Boolean

这是MySql Server问题吗?一个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,因为它是我可以编译的唯一一个在中等/部分推力的共享主机上运行。
  • 我实际上是通过List<DbDataRecord>扩展方法将读者“投射”到.Cast<>()以获取“已被删除的读者”,但它不会以任何方式更改基础数据。

2 个答案:

答案 0 :(得分:2)

我也可以使用NET Connector 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(fieldName)的问题。
我用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);
}

答案 1 :(得分:1)

我不确定这是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;
        }
    }
}

我无法进行通用转换,因为当union上的两个查询返回行时它会更改类型,所以我写了一些条件来克服这个问题。

请注意,对于MySqlDataReader,您必须更改扩展方法签名和方法名称:

GetBooleanEx(this MySqlDataReader rec, string fieldName)