2009-05-21 8 views
6

Es scheint keine Peek Methode auf dem DataReader in ado.net zu sein. Ich möchte in der Lage sein, einige einmalige Verarbeitung durchzuführen, bevor ich meinen Leser durchlaufe, und es wäre nett, in der Lage zu sein, die Daten in der ersten Zeile zu betrachten, ohne dass sie von der nachfolgenden Iteration übersprungen werden. Was ist der beste Weg, dies zu erreichen?Wie implementiere ich eine Peek() - Funktion auf einem DataReader?

Ich verwende eine SqlDataReader, aber vorzugsweise wäre die Implementierung so allgemein wie möglich (d. H. Gilt für IDataReader oder DbDataReader).

Antwort

5

würde ich etwas Ähnliches wie Jasons Lösung vorschlagen, sondern einen Wrapper verwenden, die IDataReader stattdessen implementiert, 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' 
} 

Beachten Sie, dass dies ein ganz erfordert wenig Pass-Through-Code für alle nicht betroffenen Eigenschaften, Der Vorteil ist jedoch, dass es sich um eine generische Abstraktion handelt, die an jeder Position in der Ergebnismenge "spähen" kann, ohne bei der nachfolgenden "Lese" -Operation vorwärts zu gehen.

zu benutzen:

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

Beachten Sie aber, dass Anruf jeden ‚Peek()‘ wird tatsächlich auf den nächsten Datensatz verschieben, wenn die vorherige Operation nicht auch eine ‚Peek()‘ ist. Wenn Sie diese Symmetrie mit der Operation 'Read()' beibehalten, erhalten Sie eine einfachere Implementierung und eine elegantere API.

+1

Ihr Beispiel funktioniert nicht, da die 'IDataReader' Schnittstelle selbst nicht Ihre' .Peek' Methode enthält. Sie sollten die benutzende Bereichsvariable explizit als 'PeekDataReader' eingeben oder' var' verwenden. – julealgon

3

Sie benötigen keine Peek() - Methode. Sie können mit einer Do While-Schleife erreichen, was Sie brauchen.

Also statt

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

Sie würden

dr.read(); 
... do stuff 

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

Peek() ist ein wenig flexibler als dieser Ansatz, da es mehrfach an demselben DataReader an vielen Stellen durchgeführt werden kann, ohne dass die "ersten Mal" -Ergebnisse umgangen werden müssen. –

+1

Das Lesen der DataReader-Werte verschiebt sie nicht nach vorne, die Read() -Methode tut dies. Sie können die Werte beliebig oft lesen, ohne sie zu verschieben. Ich bin mir nicht sicher, wie Peek() überhaupt irgendeinen Wert hinzufügen würde. Ich habe deinen Standpunkt vielleicht verpasst. ???? –

+0

Dies ist praktikabel, aber ich wollte den Reader über linq statt manuell konsumieren. –

4

Sie könnten eine Zustandsmaschine erstellen, die Peek-Modus vs regular-Modus verfolgt. so etwas wie dies vielleicht (könnte werfen sie einfach alle in eine einzige Datei namens Peeker.cs oder so ähnlich):

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

Art Overkill, aber na ja.

es nutzen zu können, nur gehen Sie wie folgt würden:

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

Dann tun, was Sie mit Ihrer Reihe tun müssen. Es könnte einen besseren Weg geben als ein Objekt-Array, aber Sie bekommen den Punkt.

Viel Glück!

+0

Das ist mehr oder weniger was ich wollte. Sieht aus, als würde ich eine Wrapper-Klasse benötigen, damit ich Statusinformationen behalten kann. Ich bin mir nicht sicher, ob es den Aufwand wert ist. –

+0

Hängt davon ab, wie wichtig es ist. Es würde nicht zu viel Code erfordern, aber es würde ein wenig Arbeit erfordern (3 kleine Klassen wahrscheinlich). –

+0

Nun machen Sie diese 4 kleine Klassen =) –

Verwandte Themen