【问题标题】:Does Mono treat System.Data.SqlCommands differently to .NET?Mono 对 System.Data.SqlCommands 的处理方式是否与 .NET 不同?
【发布时间】:2011-12-12 17:25:00
【问题描述】:

我在移植到 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)。

【问题讨论】:

  • 你有一个失败的具体例子吗?
  • 如果你使用(Int32)reader["column1"]会怎样
  • 您能否确认在这两种情况下您使用的是同一个 MS SQL Server 数据库?
  • ...而您的第三次阅读在列名中有一个流氓空格 -- row.GetOrdinal("column ") -- 这不是您的问题的原因吗?
  • @JonSkeet:我已经相应地更新了问题。

标签: c# .net mono sqlcommand system.data


【解决方案1】:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

    首先,你有以下代码:

    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 团队通常非常愿意修复这些问题。我强烈建议将此作为错误提交。

    希望这会有所帮助!

    【讨论】:

    • 我在最初的问题中并不清楚这一点 - 我相信是 GetOrdinal 失败了。我将重试以缩小范围。
    • 请参阅上文以更好地解释我的问题 - GetValue 调用表明我返回的数据集确实很奇怪。
    【解决方案3】:

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

    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 类来防止恶意参数删除表等。

    【讨论】:

    • 我会试一试,但我的印象是,collection[index] 符号只是语法糖,并且在底层调用与 collection.GetValue(index) 相同。
    【解决方案4】:

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

    【讨论】:

    • 我无法完全控制我正在使用的机器,所以这不是最简单的选择,但如果调查证明没有结果,我一定会考虑升级 Mono 版本 - 谢谢
    • 不幸的是,更改单声道版本并没有改变任何东西 - 无论如何谢谢
    • 太糟糕了。对不起。如果您有时间下载源代码会很有趣,请查看并执行代码分析。
    【解决方案5】:

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

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

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

    var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-05
      • 1970-01-01
      • 2019-11-19
      • 2013-05-30
      • 1970-01-01
      • 1970-01-01
      • 2014-05-14
      • 1970-01-01
      相关资源
      最近更新 更多