Mono 对待 System.Data.SqlCommands 的方式与 .NET 不同吗?

发布于 2024-12-20 23:21:04 字数 2689 浏览 1 评论 0原文

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

(仅供参考,.NET 运行时为 4.0,mono 版本为 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)。

I'm having some trouble with an application that we're porting to Mono.

(For reference, the .NET runtime is 4.0, and the mono version is 2.6.7.)

EDIT: This problem persists on Mono 2.10.2

As part of the startup of the application, it reads data into memory. For this part I'm just using in-line SQL commands, but for some reason, I'm seeing inconsistent behaviour on Linux/Mono (when the whole thing works in Windows/.NET).

I have a query that works fine in some scenarios, but not in others.

This particular example doesn't work:

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"));
}

EDIT: For comparison, here's one that does work:

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"));
}

I had possible suspicions that there were differences down to:

  • Casing (Since I know this is different on windows/linux), but putting everything to lowercase hasn't solved the problem
  • Column names (perhaps Mono cares more about reserved words?), but it seems replacing Name with [Name] or 'Name' didn't make any different

The error I had in the first case was:

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

Suggesting that there is no "column1" in the returned result set.

(EDIT: updated this section a little for clarity)

Oddly, if I do this:

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));
}

The output is:

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

I assume something strange is happening with the Mono framework in this case, but I can't find a relevant bug report, and I can't identify why this only happens in some cases but not others! Has anyone else had a similar experience?

EDIT: I've changed some of the statements to match those in the failing query exactly, in case there is an issue with reserved words or similar. Note that the query I'm issuing in the second case really does request four columns and seemingly only gets two very odd ones back (0 | New ).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

铃予 2024-12-27 23:21:04

好吧,伙计,我设法重现了你的问题。你那里有一个线程问题。这是我唯一的想法可能是什么导致了这个问题,并且我成功地重现了它。

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

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

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

    ObjectexecuteLock = new Object();
    
    私有 IDataReader ExecuteThreadSafe(IDbCommand sqlCommand)
    {
        锁(执行锁)
        {
            返回 sqlCommand.ExecuteReader(CommandBehavior.CloseConnection);
        }
    }
    

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

Ok mate I managed to reproduce your problem. You have a threading problem there. That was my only idea what may be the cause of this problem and I managed to actually reproduce it.

In order to fix it you need to do the following:

  1. Make sure every reader has a reader.Close() call after parsing the
    data.

  2. Use the following code to do thread-safe calls:

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

Looks like mono has a different implementation of SQL objects than .NET. It's true that I wasn't able to reproduce it on Windows!

断桥再见 2024-12-27 23:21:04

我会首先尝试找出差异到底在哪里。

首先,您有以下代码:

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

希望这有帮助!

I would start out by trying to figure out exactly where the difference lies.

First off, you have the following code:

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?

I believe you said the first two lines work, and the third line blows up. We first need to figure out where this blows up. I'd try:

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

Does test equal a valid column index? If not, that's your problem. Make sure that's the exact column name, try different casings, etc. If the index is valid, try:

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

to figure out what the data type of this column is. Perhaps there's a casting problem.

If that fails, I'd try loading the reader into a DataSet object instead so you can look at the exact schema more carefully.

If you do find a difference in behavior between Mono and the .NET Framework, the Mono team is usually very willing to fix these. I strongly recommend filing this as a bug.

Hope this helps!

初相遇 2024-12-27 23:21:04

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

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

Try accessing your reader in this manner:

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

Also, as a side note, make sure you're using the "using" directive for disposable objects. I'm not sure if Mono implements this, but it's worth a shot. What the using directive does is cleans up disposable objects as soon as you're done using them. It's very handy and makes for clean code. Quick example:

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

Also, if you're getting user input that goes straight to your commands, make sure you use the MySqlParameter class to keep malicious parameters from dropping tables, for example.

过潦 2024-12-27 23:21:04

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

Mono is not .NET and there are a lot of differences especially with earlier versions. The root methodology to connect to SQL is using a TDS (tabular data stream) implementation in C# which for earlier versions of Mono (and TDS as a result) can cause a lot of problems. Almost all essential classes for SQL and data are having differences that potentially may cause exceptions. Maybe it's worthy to try with Mono 2.10+ because the Mono team improves the whole project continuously.

反目相谮 2024-12-27 23:21:04

continue

I am using Mono 4.0 and I have a similar problem. My research shows, that most probable reason of this problem is flawed implementation of connection pool in Mono. At least, if I switch off pooling, the problem disappears.

To switch off connection pooling you need to add the following string to your connection string: Pooling=false;

After that, creation of connection object should look like that:

var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文