3

I'm writing some helper functions that take a SqlDataReader and process the results. Now normally you'd structure the loop like so:

while(reader.read()){
    // Read fields
}

But I my be passing the function a reader which is already positioned on a row, and in this case I want it to process the current row. The code above will only output the entire result set if read() had not been called prior to invoking the function.

I want the function to do something like this:

if(!reader._dataReady)
    rowExists = reader.read() // Read 
while(rowExists){
    // read fields
    rowExists = reader.read()
}

_dataReady is an actual member of SqlDataReader and seems to do what I need, however it's private! Surely there's a way to determine if the reader is positioned on an actual row, short of trying to read the field and catching the exception.

1
  • Can't you just force your users to always do a read before calling your helper? Failure to do so would lead to an exception which you could catch, wrap, and rethrow. Your target audience is obviously developers, so force them to follow your API. Commented Feb 22, 2012 at 1:23

4 Answers 4

2

why not just subclass SqlDataReader, and create Read() method? I have it in all my projects, now:

public class SafeDataReader : IDataReader, SqlDataReader
{
    public IDataReader reader {get;set;}
    public int GetInt32(string aFieldName, int aDefault);
    public int? GetInt32Nullable(string aFieldName);
    .... 
}
Sign up to request clarification or add additional context in comments.

1 Comment

Good idea. I could store an enum that tracked the reader position: - SafeDataReaderPosition.BeforeRows - SafeDataReaderPosition.OnRow - SafeDataReaderPosition.EndOfFile. All without having to sacrifice async performance by using a dataset. Don't know why this isn't part of the implementation already...
1

You can use the FieldCount property. As per the documentation, it returns zero if not positioned in a valid recordset.

Some other solutions:

  1. Do your Read() looping entirely outside of your helper method (and always operate only on the current row)

  2. Do your Read() looping entirely within the helper method, and always pass it fresh data readers that have not been advanced

  3. Do your first Read() outside of the data reader, continue Read() looping within the helper method, and always assume that the data reader has been advanced to the first record.

  4. Probably overkill: write a wrapping implementation of IDataReader that forwards all calls to the underlying data reader (probably passed in via the constructor), but exposes a property indicating whether Read has ever been called.

2 Comments

Just tested, and the fieldCount property does not return zero if read() hasn't been called yet. It only returns zero if SQL didn't return a result set at all.
To clarify: Trent is correct - if the reader has been populated, but Read() has not been called, and the reader has rows with fields, then FieldCount will be set to the number of fields in the result set.
1

Reflection is dangerous but often useful. Here is an extension

public static bool? DataReady(this SqlDataReader rd) {
            var sharedState = rd.GetType()?.GetField("_sharedState", 
                 BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(rd);
            return (bool?)sharedState?.GetType().GetField("_dataReady",
                BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(sharedState);
 }

Information taken from SqlDataReader referencesource.

Comments

0

It is sort of overloading the purpose, but the following should work:

rowExists = reader.FieldCount > 0;

I suspect that you are going to find yourself having problems with a design that has you doing Read()s in different locations in your code. Try doing only data manipulation in your method (and passing it as an IDataRecord instead of an IDataReader), and leave the record traversal logic to the caller.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.