Есть ли ошибка в SqlDataReader.HasRows при работе с SQL Server 2008? - PullRequest
20 голосов
/ 27 января 2009

Посмотрите на эти два запроса:

-- #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 будет иметь значение false.

Это известная ошибка в .NET и / или SQL Server, или я пропустил что-то очевидное?
Я использую VS2008SP1, .NET3.5SP1 и SQL2008.

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

Ответы [ 6 ]

6 голосов
/ 06 февраля 2009

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

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

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

Само собой разумеется, что я избегаю .HasRows теперь;)

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

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

2 голосов
/ 27 января 2009

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

Свойство HasRows инкапсулирует поле _hasRows, которое устанавливается в true или false внутри SqlDataReader во многих разных местах по многим различным причинам. В большинстве этих мест он установлен на true, если закрытый метод TdsParserStateObject 'PeekByte возвращает число, указывающее на наличие данных.

1 голос
/ 04 февраля 2009

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

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

0 голосов
/ 04 декабря 2009

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

0 голосов
/ 04 февраля 2009

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

using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleResult))
0 голосов
/ 27 января 2009

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

В прошлом я замечал похожие проблемы с HasRows и пошел по схеме, аналогичной моей, перечисленной выше, с минимальными проблемами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...