Есть ли ошибка в 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 строк, но я также вижу следующее на вкладке Сообщения:

Информационный: условие полнотекстового поиска содержало шумовые слова.

Пока что так скучно - именно то, что я ожидал.

Теперь взгляните на этот фрагмент C#:

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.

РЕДАКТИРОВАТЬ: Я ценю, что мой вопрос очень похож на этот, и это почти наверняка проявление той же самой проблемы, но этот вопрос был затоплен в течение трех месяцев без однозначного ответа.

6 ответов

Я являюсь автором заданного вопроса (потерял логин), и мне так и не удалось это выяснить. В конце я положил это на плохое вуду, пожертвовал опрятностью и пошел с чем-то вроде

bool readerHasRows=false;
while(reader.reader())
{
   readerHasRows=true;
   doStuffOverAndOver();
}
if (!readerHasRows)
{
   probablyBetterShowAnErrorMessageThen();
}

Что было действительно странно, так это то, что он работал на одной странице aspx, а не на другой, несмотря на то, что блоки кода были почти идентичны, кроме используемой хранимой процедуры.

Излишне говорить, что теперь я избегаю.HasRows;)

РЕДАКТИРОВАТЬ - Management Studio также показывает сообщения на вкладке сообщений о проблемной процедуре в моем проекте. Так что, похоже, причина проблемы. Но с чего бы это взбалтывать.HasRows??

EDIT2 - подтвержден, изменен запрос, чтобы избежать появления предупреждающих сообщений и.hasrows теперь верно.

Это, конечно, странное поведение, но мне интересно, почему вы должны проверить HasRows если вы собираетесь просто повторять набор результатов.

HasRows свойство инкапсулирует _hasRows поле, которое устанавливается в true или же false внутри SqlDataReader во многих разных местах по разным причинам. Большинство из этих мест это установлено true если частный TdsParserStateObject"s PeekByte Метод возвращает число, которое указывает на наличие данных.

Похоже, HasRows является одним из тех свойств, значение которых не гарантируется, чтобы быть точным...

Я согласен с предыдущими двумя постами (о переходе прямо в while(dr.Read()) и получаю ординалы в первой итерации). Кроме того, почему бы не получить набор данных вместо чтения данных? Если в этом случае вы имеете дело только с 20 строками, одновременное получение всего набора данных может не сильно повлиять на производительность по сравнению с использованием устройства чтения данных. Я знаю, что на самом деле это не ответ на ваш вопрос, а просто мысль об обходном пути.

Как и в случае с Эндрю, я не уверен, почему бы вам не использовать цикл while, чтобы избежать снижения производительности при дополнительных вызовах GetOrdinal. Вы можете использовать значение флага для выполнения вызовов GetOrdinal в первой строке, а затем после этого код можно пропустить.

Я заметил подобные проблемы с HasRows в прошлом, и пошел к шаблону, подобному моему, перечисленному выше, с минимальными проблемами.

Я бы посоветовал вызвать метод NextResult, если есть подозрение, что множественные результирующие наборы вызывают эту проблему. Поскольку первый набор результатов является пустым, использование CommandBehavior.SingleResult не изменит поведения, поскольку первый (пустой) результат все равно будет возвращен. Вы можете попробовать это. Во всяком случае, я слышал, что это ошибка, но я не помню, где я ее прочитал, и быстрый поиск в Google не дал никаких результатов.

Попробуйте следующее и сообщите, если оно прошло успешно:

using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleResult))
Другие вопросы по тегам