Mono рассматривает System.Data.SqlCommands иначе, чем .NET? - PullRequest
4 голосов
/ 12 декабря 2011

У меня возникли проблемы с приложением, которое мы переносим на 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', кажется, не изменила

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

[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 ]

2 голосов
/ 19 декабря 2011

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

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

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

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

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

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

1 голос
/ 12 декабря 2011

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

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

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

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

0 голосов
/ 14 сентября 2015

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

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

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

var conn = new SqlConnection("Server=localhost; Database=somedb; user=user1; password=qwerty; Pooling=false;");
0 голосов
/ 12 декабря 2011

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

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

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

0 голосов
/ 12 декабря 2011

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

...