How to implement Peek () function in DataReader?

There is no Peek method in DataReader on ado.net. I would like to be able to do some one-time processing before I go through my reader, and it would be nice to be able to look at the data in the first line without causing it to be skipped by a subsequent subsequent iteration. What is the best way to do this?

I use SqlDataReader , but preferably the implementation will be as general as possible (i.e. apply to IDataReader or DbDataReader ).

+6
source share
3 answers

I would suggest something similar to Jason's solution, but using a wrapper that implements IDataReader, like this:

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

Please note that this requires quite a lot of cross-cutting code for all unaffected properties, but the advantage is that it is a general abstraction that can β€œpeek” at any position in the result set without moving forward on the subsequent β€œread” operation.

For 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 that each call to "Peek ()" will actually advance to the next record if the previous operation was not also "Peek ()". Preserving this symmetry with the "Read ()" operation provides a simpler implementation and a more elegant API.

+5
source share

You can create a state machine that tracks peek-mode vs regular-mode. Maybe something like this (maybe just drop them all into a single file called Peeker.cs or something like that):

 public sealed class Peeker { internal readonly PeekMode PEEKING; internal readonly NormalMode NORMAL; private ReadState _state; public Peeker() { PEEKING = new PeekMode(this); NORMAL = new NormalMode(this); // Start with a normal mode _state = NORMAL; } public object[] OnRead(IDataReader dr, bool peek) { return _state.OnRead(dr, peek); } internal void SetState(ReadState state) { _state = state; } } internal abstract class ReadState { protected Peeker _peeker; protected ReadState(Peeker p) { _peeker = p; } public abstract object[] OnRead(IDataReader dr, bool peek); } internal class PeekMode : ReadState { public PeekMode(Peeker p) : base(p) { } public override object[] OnRead(IDataReader dr, bool peek) { object[] datarow = new object[dr.FieldCount]; if (peek) { dr.GetValues(datarow); } else { if (dr.Read()) { dr.GetValues(datarow); _peeker.SetState(_peeker.NORMAL); } } return datarow; } } internal class NormalMode : ReadState { public NormalMode(Peeker p) : base(p) { } public override object[] OnRead(IDataReader dr, bool peek) { object[] datarow = new object[dr.FieldCount]; if (peek) { if (dr.Read()) { dr.GetValues(datarow); _peeker.SetState(_peeker.PEEKING); } } else { if (dr.Read()) { dr.GetValues(datarow); } } return datarow; } } 

Kind of redundant, but good.

To use it, you simply do the following:

 Peeker p = new Peeker(); . . . SomeDataReaderType dr = SomeCommandType.ExecuteReader(); . . . // To peek object[] myDataRow = p.OnRead(dr, true); // or not to peek object[] myDataRow = p.OnRead(dr, false); 

Then do what you need to do with your string. Perhaps a better way than using an array of objects, but you get the point.

Good luck

+4
source share

You do not need the Peek () method. You can accomplish what you need in a Do While loop.

So instead

 while(dr.read()) { ... do stuff } 

You would

 dr.read(); ... do stuff do { ... do stuff }while(dr.read()) 
+3
source share

All Articles