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 ответов
Хорошо, приятель, мне удалось воспроизвести твою проблему. У вас там проблема с потоками. Это была моя единственная идея, что может быть причиной этой проблемы, и мне удалось воспроизвести ее.
Чтобы это исправить, нужно сделать следующее:
Убедитесь, что у каждого читателя есть вызов reader.Close() после анализа данных.
Используйте следующий код для выполнения потоковых вызовов:
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, чтобы, например, злонамеренные параметры не сбрасывали таблицы.