Вопрос

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 ).

Это было полезно?

Решение

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!

Другие советы

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!

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.

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.

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;");
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top