Mono рассматривает System.Data.SqlCommands иначе, чем.NET?

У меня возникли проблемы с приложением, которое мы переносим на 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 больше заботится о зарезервированных словах?), Но замена имени на [Имя] или "Имя", кажется, не изменила

Ошибка, которую я имел в первом случае, была:

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

5 ответов

Решение

Хорошо, приятель, мне удалось воспроизвести твою проблему. У вас там проблема с потоками. Это была моя единственная идея, что может быть причиной этой проблемы, и мне удалось воспроизвести ее.

Чтобы это исправить, нужно сделать следующее:

  1. Убедитесь, что у каждого читателя есть вызов reader.Close() после анализа данных.

  2. Используйте следующий код для выполнения потоковых вызовов:

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

Похоже, у mono реализация SQL-объектов отличается от реализации в.NET. Это правда, что я не смог воспроизвести его на Windows!

Я бы начал с попытки выяснить, в чем именно заключается разница.

Во-первых, у вас есть следующий код:

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, как правило, очень готова исправить это. Я настоятельно рекомендую подать это как ошибку.

Надеюсь это поможет!

Я использую Mono 4.0 и у меня похожая проблема. Мои исследования показывают, что наиболее вероятной причиной этой проблемы является некорректная реализация пула соединений в Mono. По крайней мере, если я выключу пул, проблема исчезнет.

Чтобы отключить пул соединений, вам нужно добавить следующую строку в строку соединения: Pooling=false;

После этого создание объекта подключения должно выглядеть так:

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

Mono не является.NET, и есть много различий, особенно с более ранними версиями. Корневая методология подключения к SQL использует реализацию TDS (поток табличных данных) в C#, что для более ранних версий Mono (и в результате TDS) может вызвать много проблем. Почти все основные классы для SQL и данных имеют различия, которые могут вызвать исключения. Возможно, стоит попробовать Mono 2.10+, потому что команда Mono постоянно совершенствует весь проект.

Попробуйте получить доступ к вашему читателю следующим образом:

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

Также, в качестве примечания, убедитесь, что вы используете директиву "использование" для одноразовых предметов. Я не уверен, что Моно реализует это, но это стоит того. Директива 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, чтобы, например, злонамеренные параметры не сбрасывали таблицы.

Другие вопросы по тегам