Есть ли ошибка в SqlDataReader.HasRows при работе с SQL Server 2008?
Взгляните на эти два вопроса:
-- #1
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'monkey')
-- #2
SELECT * FROM my_table
WHERE CONTAINS(my_column, 'a OR monkey') -- "a" is a noise word
ЗапроС# 1 возвращает 20 строк, когда я запускаю его в Management Studio.
Запрос № 2 возвращает те же 20 строк, но я также вижу на вкладке "Сообщения" следующее:
Информационное: условие полнотекстового поиска содержит слово шума.
До сих пор так скучно - именно то, что я ожидал бы.
Теперь посмотрим на этот фрагмент С#:
using (SqlConnection conn = new SqlConnection(...))
{
SqlCommand cmd = conn.CreateCommand();
// setup the command object...
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
if (dr.HasRows)
{
// get column ordinals etc...
while (dr.Read())
{
// do something useful...
}
}
}
}
Когда я запускаю этот код по запросу # 1, все ведет себя так, как ожидалось, секция "делать что-то полезное" попадает для каждой из 20 строк.
Когда я запускаю его против запроса №2, ничего не происходит - раздел "сделать что-то полезное" никогда не достигается.
Теперь, когда вещи становятся немного интереснее...
Если я удалю проверку HasRows
, тогда все будет работать так, как ожидалось, секция "сделать что-то полезное" попадет для каждой из 20 строк, независимо от того, какой запрос используется.
Похоже, что свойство HasRows
неправильно заполнено, если SQL Server генерирует сообщение. Результаты возвращаются и могут быть повторены с помощью метода Read()
, но свойство HasRows
будет ложным.
Является ли это известной ошибкой в .NET и/или SQL Server, или я пропустил что-то очевидное?
Я использую VS2008SP1,.NET3.5SP1 и SQL2008.
EDIT: Я понимаю, что мой вопрос очень похож на этот, и это почти наверняка является проявлением той же проблемы, но этот вопрос увяз в течение трех месяцев без окончательного ответа.
Ответы
Ответ 1
Я являюсь оригинальным плакатом заданного вопроса (потерянный логин) и так и не смог разобраться. В конце концов я положил его на плохое вуду, пожертвовал аккуратность и пошел с чем-то вроде
bool readerHasRows=false;
while(reader.reader())
{
readerHasRows=true;
doStuffOverAndOver();
}
if (!readerHasRows)
{
probablyBetterShowAnErrorMessageThen();
}
Что было действительно странно, так это то, что он работал на одной странице aspx, а не в другой, несмотря на то, что кодовые блоки почти идентичны используемой хранимой процедуре.
Излишне говорить, что я избегаю .HasRows с этого момента;)
EDIT. Студия Management Studio также отображает сообщения на вкладке сообщений в процедуре проблем в моем проекте. Таким образом, это, по-видимому, является причиной проблемы. Но почему это должно было случиться .HasRows??
EDIT2. Подтвержденный, изменил запрос, чтобы избежать предупреждений, а hrows теперь прав.
Ответ 2
Это, безусловно, странное поведение, но мне интересно, почему вам нужно проверить HasRows
, если вы собираетесь просто перебирать результирующий набор.
Свойство HasRows
инкапсулирует поле _hasRows
, которое по множеству разных причин устанавливается в true
или false
внутри SqlDataReader
во многих разных местах. В большинстве этих мест он установлен на true
, если частный метод TdsParserStateObject
PeekByte
возвращает число, указывающее на наличие данных.
Ответ 3
Похоже, что HasRows является одним из тех свойств, значение которого не гарантируется точно...
Я согласен с предыдущими двумя сообщениями (о том, чтобы идти прямо во время (dr.Read()) и получить ординалы на первой итерации). Кроме того, почему бы не получить набор данных вместо устройства чтения данных? Если в этом случае вы имеете дело только с 20 строками, получение всего набора данных сразу может не сильно повлиять на производительность по сравнению с использованием устройства чтения данных. Я знаю, что это на самом деле не отвечает на ваш вопрос, а просто мысль об обходном пути.
Ответ 4
Как и в случае с Andrew, я не уверен, почему вы не просто используете цикл while, чтобы избежать повышения производительности дополнительных вызовов GetOrdinal. вы можете использовать значение флага для выполнения вызовов GetOrdinal в первой строке, после чего код можно пропустить.
Я заметил аналогичные проблемы с HasRows в прошлом и пошел к шаблону, подобному моему, указанному выше с минимальными проблемами.
Ответ 5
Попробуйте следующее и сообщите, было ли это успешным:
using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleResult))
Ответ 6
Я бы предложил вызвать метод NextResult, если, как подозревают, возникли проблемы с несколькими наборами результатов. Поскольку первый набор результатов - тот, который кажется пустым, использование CommandBehavior.SingleResult не изменило бы поведение, так как первый (пустой) результат все равно будет возвращен. Вы могли бы попробовать это. Во всяком случае, я слышал, что это была ошибка, но я не помню, где я ее читал, и быстрый поиск в Google не дал никаких результатов.