Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a bug in SqlDataReader.HasRows when running against SQL Server 2008?

Tags:

Take a look at these two queries:

-- #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

Query #1 returns 20 rows when I run it in Management Studio.
Query #2 returns the same 20 rows, but I also see the following in the Messages tab:

Informational: The full-text search condition contained noise word(s).

So far, so boring - exactly what I'd expect to happen.

Now, take a look at this C# snippet:

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...
            }
        }
    }
}

When I run this code against query #1 everything behaves as expected - the "do something useful" section gets hit for each of the 20 rows.

When I run it against query #2, nothing happens - the "do something useful" section is never reached.

Now here's where things get a bit more interesting...

If I remove the HasRows check then everything works as expected - the "do something useful" section gets hit for each of the 20 rows, regardless of which query is used.

It seems that the HasRows property isn't populated correctly if SQL Server generates a message. The results are returned and can be iterated through using the Read() method but the HasRows property will be false.

Is this a known bug in .NET and/or SQL Server, or have I missed something obvious?
I'm using VS2008SP1, .NET3.5SP1 and SQL2008.

EDIT: I appreciate that my question is very similar to this one, and it's almost certainly a manifestation of the same issue, but that question has been bogged down for three months with no definitive answer.

like image 523
LukeH Avatar asked Jan 27 '09 15:01

LukeH


People also ask

Why do we use SqlDataReader?

It is used to populate an array of objects with the column values of the current row. It is used to get the next result, when reading the results of SQL statements. It is used to read record from the SQL Server database. To create a SqlDataReader instance, we must call the ExecuteReader method of the SqlCommand object.

What is SQL Server DataReader?

A DataReader parses a Tabular Data Stream from Microsoft SQL Server, and other methods of retrieving data from other sources. A DataReader is usually accompanied by a Command object that contains the query, optionally any parameters, and the connection object to run the query on.


2 Answers

I'm the original poster of the refernced question (lost login) and never managed to figure it out. In the end I put it down to bad voodoo, sacrificed neatness and went with something like

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

What was really weird was that it worked in one aspx page and not in a another despite the code blocks being almost identical bar the stored procedure used.

Needless to say I'm avoiding .HasRows from now on ;)

EDIT - Management Studio shows messages in the messages tab on the problem procedure in my project too. So that seems to be the cause of the problem. But why would it bugger up .HasRows??

EDIT2 - Confirmed, altered the query to avoid the warning messages and .hasrows is now true.

like image 67
ctrlalt3nd Avatar answered Sep 19 '22 02:09

ctrlalt3nd


That certainly is strange behavior - but I am wondering why you need to check HasRows if you are going to simply iterate the result-set.

The HasRows property encapsulates a _hasRows field which gets set in to true or false inside SqlDataReader in many different places for many different reasons. Most of these places it is set to true if the private TdsParserStateObject's PeekByte method returns a number that indicates data is present.

like image 39
Andrew Hare Avatar answered Sep 20 '22 02:09

Andrew Hare