There doesn't seem to be a Peek
method on the DataReader in ado.net. I would like to be able to perform some one-off processing before I loop through my reader, and it would be nice to be able to look at the data in the first row without causing it to be skipped by the subsequent iteration. What is the best way to accomplish this?
I am using a SqlDataReader
, but preferably the implementation would be as general as possible (i.e. apply to IDataReader
or DbDataReader
).
As you've seen in the previous examples, you call the ExecuteReader method of the Command object, which returns an instance of the 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.
The Read() method in the DataReader is used to read the rows from DataReader and it always moves forward to a new valid row, if any row exist . There are two types of DataReader in ADO.NET. They are SqlDataReader and the OleDbDataReader. The System.
SqlDataReader objects allow you to read data in a fast forward-only manner. You obtain data by reading each row from the data stream. Call the Close method of the SqlDataReader to ensure there are not any resource leaks.
I would suggest something similar to Jason's solution, but using a wrapper that implements IDataReader instead, so:
sealed public class PeekDataReader : IDataReader
{
private IDataReader wrappedReader;
private bool wasPeeked;
private bool lastResult;
public PeekDataReader(IDataReader wrappedReader)
{
this.wrappedReader = wrappedReader;
}
public bool Peek()
{
// If the previous operation was a peek, do not move...
if (this.wasPeeked)
return this.lastResult;
// This is the first peek for the current position, so read and tag
bool result = Read();
this.wasPeeked = true;
return result;
}
public bool Read()
{
// If last operation was a peek, do not actually read
if (this.wasPeeked)
{
this.wasPeeked = false;
return this.lastResult;
}
// Remember the result for any subsequent peeks
this.lastResult = this.wrappedReader.Read();
return this.lastResult;
}
public bool NextResult()
{
this.wasPeeked = false;
return this.wrappedReader.NextResult();
}
// Add pass-through operations for all other IDataReader methods
// that simply call on 'this.wrappedReader'
}
Note that this does require quite a bit of pass-through code for all the unaffected properties, but the benefit is that it is a generic abstraction that can 'peek' at any position in the result set without moving forward on the subsequent 'read' operation.
To use:
using (IDataReader reader = new PeekDataReader(/* actual reader */))
{
if (reader.Peek())
{
// perform some operations on the first row if it exists...
}
while (reader.Read())
{
// re-use the first row, and then read the remainder...
}
}
Note though that every 'Peek()' call will actually move to the next record if the previous operation was not also a 'Peek()'. Keeping this symmetry with the 'Read()' operation provides a simpler implementation and a more elegant API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With