2009-05-21 9 views
6

No parece haber un método Peek en DataReader en ado.net. Me gustaría poder realizar un proceso único antes de pasar por mi lector, y sería bueno poder ver los datos en la primera fila sin hacer que la iteración siguiente los omita. Cuál es la mejor manera de lograr esto?¿Cómo implemento una función Peek() en un DataReader?

Estoy usando un SqlDataReader, pero preferiblemente la implementación sería lo más general posible (es decir, se aplica a IDataReader o DbDataReader).

Respuesta

5

Yo sugeriría algo similar a la solución de Jason, pero utilizando un envoltorio que implementa IDataReader lugar, por lo que:

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

Tenga en cuenta que esto requiere un poco de código de paso a través de todas las propiedades afectadas, pero el beneficio es que es una abstracción genérica que puede 'mirar' en cualquier posición en el conjunto de resultados sin avanzar en la operación de 'lectura' posterior.

de usar:

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

Nota sin embargo que cada llamada 'Peek()' en realidad moverse al siguiente registro, si la operación anterior no era también un 'Peek()'. Mantener esta simetría con la operación 'Read()' proporciona una implementación más simple y una API más elegante.

+1

Tu ejemplo no funciona, ya que la interfaz 'IDataReader' en sí misma no contiene tu método' .Peek'. Debería escribir explícitamente la variable de ámbito de uso como 'PeekDataReader' o usar' var'. – julealgon

3

No necesita un método Peek(). Puede lograr lo que necesita con un ciclo Do While.

Así que en lugar de

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

Usted habría

dr.read(); 
... do stuff 

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

Peek() es bastante más flexible que este enfoque, ya que se puede realizar varias veces en el mismo DataReader, en muchos lugares, sin tener que pasar los resultados de "primera vez". –

+1

Al leer los valores de DataReader no se avanza, el método Read() lo hace. Puede leer los valores tantas veces como quiera sin moverlo hacia adelante. No estoy seguro de cómo Peek() agregaría ningún valor. Puede que haya perdido tu punto. ???? –

+0

Esto es factible, pero quería consumir el lector a través de linq en lugar de consumirlo manualmente. –

4

Se puede crear una máquina de estado que realiza el seguimiento peek-vs-modo de modo regular. Tal vez algo como esto (podría simplemente tirar a todos en un solo archivo llamado Peeker.cs o algo por el estilo):

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

tipo de exageración, pero bueno.

para usarlo usted acaba de hacer lo siguiente:

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); 

Luego hacer lo que tiene que ver con su fila. Puede haber una manera mejor que usar una matriz de objetos, pero entiendes el punto.

¡Buena suerte!

+0

Esto es más o menos lo que estaba buscando. Parece que necesitaría una clase contenedora para poder guardar la información del estado. Aunque no estoy seguro si vale la pena la molestia. –

+0

Depende de lo importante que sea. No tomaría demasiado código para hacerlo, pero tomaría un poco de trabajo (3 clases pequeñas probablemente). –

+0

Bueno, haz 4 clases pequeñas =) –

Cuestiones relacionadas