4

我在移植到 Mono 的应用程序时遇到了一些问题。

(作为参考,.NET 运行时为 4.0,单声道版本为 2.6.7。)

编辑:这个问题在 Mono 2.10.2 上仍然存在

作为应用程序启动的一部分,它将数据读入内存。对于这一部分,我只是使用内联 SQL 命令,但由于某种原因,我在 Linux/Mono 上看到了不一致的行为(当整个事情在 Windows/.NET 中运行时)。

我有一个查询在某些情况下可以正常工作,但在其他情况下却不行。

这个特定的例子不起作用:

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn);
var reader = cmd.ExecuteReader();

var objectToLoad = new SomeObjectType();

while (reader.Read())
{
    objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("ID"));
    objectToLoad.Property2 = reader.GetString(row.GetOrdinal("Name"));
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATTerritoryID"));
    objectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("NativeCurrencyID"));
}

编辑:为了比较,这是一个有效的

var cmd = new SqlCommand("SELECT VATTerritoryID, ProductDescriptionID, VATBandID FROM VATTerritoryBandExceptions", conn);
var reader = cmd.ExecuteReader();

var someOtherObjectToLoad = new SomeOtherObjectType();

while (reader.Read())
{
    someOtherObjectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("VATTerritoryID"));
    someOtherObjectToLoad.Property2 = reader.GetString(row.GetOrdinal("ProductDescriptionID"));
    someOtherObjectToLoad.Property3 = reader.GetInt32(row.GetOrdinal("VATBandID"));
}

我可能怀疑存在以下差异:

  • 大小写(因为我知道这在 windows/linux 上有所不同),但是将所有内容都小写并没有解决问题
  • 列名(也许 Mono 更关心保留字?),但似乎用 [Name] 或 'Name' 替换 Name 并没有什么不同

我在第一种情况下的错误是:

[IndexOutOfRangeException: Array index is out of range.]
at System.Data.SqlClient.SqlDataReader.GetInt32(Int32 i)

提示返回的结果集中没有“column1”。

(编辑:为了清楚起见,稍微更新了本节)

奇怪的是,如果我这样做:

var cmd = new SqlCommand("SELECT ID, Name, VATTerritoryID, NativeCurrencyID FROM PricingZones", conn);
var reader = cmd.ExecuteReader();

var objectToLoad = new SomeObjectType();

while (reader.Read())
{
    Console.WriteLine("First row, first column is " + row.GetValue(0));
    Console.WriteLine("First row, second column is " + row.GetValue(1));
    Console.WriteLine("First row, third column is " + row.GetValue(2));
    Console.WriteLine("First row, fourth column is " + row.GetValue(3));
}

输出是:

First row, first column is 0
First row, second column is New
Array index is out of range.

我假设在这种情况下 Mono 框架发生了一些奇怪的事情,但我找不到相关的错误报告,我无法确定为什么这只发生在某些情况下而不是其他情况下!有没有其他人有过类似的经历?

编辑:我已经更改了一些语句以完全匹配失败查询中的语句,以防保留字或类似内容出现问题。请注意,我在第二种情况下发出的查询确实请求了四列,并且似乎只返回了两个非常奇怪的列(0 | New)。

4

5 回答 5

2

好的,伙计,我设法重现了您的问题。你有一个线程问题。那是我唯一的想法可能是导致此问题的原因,并且我设法实际重现了它。

为了修复它,您需要执行以下操作:

  1. 确保每个读者在解析数据后都有一个 reader.Close() 调用。

  2. 使用以下代码进行线程安全调用:

    Object executeLock = new Object();
    
    private IDataReader ExecuteThreadSafe(IDbCommand sqlCommand)
    {
        lock (executeLock)
        {
            return sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }
    

看起来 mono 的 SQL 对象实现与 .NET 不同。确实,我无法在 Windows 上重现它!

于 2011-12-19T11:08:40.170 回答
1

我会首先尝试找出差异所在。

首先,您有以下代码:

objectToLoad.Property1 = reader.GetInt32(row.GetOrdinal("column1"));
objectToLoad.Property2 = reader.GetString(row.GetOrdinal("column2"));
objectToLoad.Property3 = reader.GetString(row.GetOrdinal("column ")); //Is the space on the end intended?

我相信你说前两行有效,第三行爆炸了。我们首先需要弄清楚这在哪里爆发。我会尝试:

int test = row.GetOrdinal("column ");

是否test等于有效的列索引?如果不是,那是你的问题。确保这是准确的列名,尝试不同的大小写等。如果索引有效,请尝试:

object foo = reader[test];
Console.WriteLine(foo.GetType().Name);

弄清楚该列的数据类型是什么。也许有一个铸造问题。

如果失败,我会尝试将阅读器加载到DataSet对象中,以便您可以更仔细地查看确切的架构。

如果您确实发现 Mono 和 .NET Framework 之间的行为存在差异,Mono 团队通常非常愿意修复这些问题。我强烈建议将此作为错误提交。

希望这可以帮助!

于 2011-12-12T17:42:14.083 回答
0

Mono 不是 .NET,并且存在很多差异,尤其是与早期版本。连接到 SQL 的根本方法是使用 C# 中的 TDS(表格数据流)实现,这对于早期版本的 Mono(以及因此的 TDS)可能会导致很多问题。几乎所有 SQL 和数据的基本类都存在可能导致异常的差异。也许值得尝试使用 Mono 2.10+,因为 Mono 团队不断改进整个项目。

于 2011-12-12T17:41:14.077 回答
0

尝试以这种方式访问​​您的阅读器:

reader["column1isastring"].ToString();
(Int32)reader["column2isInt32"];

另外,作为旁注,请确保您对一次性物品使用“使用”指令。我不确定 Mono 是否实现了这一点,但值得一试。using 指令的作用是在您使用完一次性对象后立即清理它们。它非常方便,并且可以生成干净的代码。快速示例:

using (MySqlCommand command = new MySqlCommand("SELECT column1, column2, column FROM tablename", conn))
{
    try
    {
        conn.Open();
        using (MySqlDataReader reader = command.ExecuteReader())
        {
                reader.Read();
                var someString = reader["column1isastring"].ToString();
                var whatever = (Int32)reader["column2isInt32"];
        } //reader closes and disposes here
    }
    catch (Exception ex)
    {
        //log this exception
    }
    finally
    {
        conn.Close();
    }
} //conn gets closed and disposed of here

此外,如果您获得的用户输入直接进入您的命令,请确保使用 MySqlParameter 类来防止恶意参数删除表,例如。

于 2011-12-12T17:50:41.860 回答
0

我正在使用 Mono 4.0,我也有类似的问题。我的研究表明,这个问题最可能的原因是 Mono 中连接池的实现存在缺陷。至少,如果我关闭池,问题就会消失。

要关闭连接池,您需要将以下字符串添加到连接字符串:Pooling=false;

之后,连接对象的创建应如下所示:

var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;");
于 2015-09-14T18:53:05.627 回答