Mono是否将System.Data.SqlCommands与.NET区别对待?

时间:2011-12-12 17:25:01

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

我在使用我们移植到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"));
}

我可能怀疑存在差异:

  • Casing(因为我知道这在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 |新)。

5 个答案:

答案 0 :(得分:2)

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

为了解决这个问题,您需要执行以下操作:

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

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

    Object executeLock = new Object();
    
    private IDataReader ExecuteThreadSafe(IDbCommand sqlCommand)
    {
        lock (executeLock)
        {
            return sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }
    
  3. 看起来mono具有与.NET不同的SQL对象实现。确实,我无法在Windows上重现它!

答案 1 :(得分: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团队通常非常愿意解决这些问题。我强烈建议将此归档为错误。

希望这有帮助!

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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

另外,作为附注,请确保您对一次性对象使用“using”指令。我不确定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类来防止恶意参数丢弃表格。

答案 4 :(得分:0)

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

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

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

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