2009-05-21 8 views
6

ado.netのDataReaderにはPeekメソッドがないようです。私は読者をループする前にいくつかの一回限りの処理を行うことができたいと思います。最初の行のデータを後続の繰り返しでスキップすることなく見ることができるといいでしょう。これを達成する最良の方法は何ですか?DataReaderでPeek()関数を実装するにはどうすればよいですか?

私はSqlDataReaderを使用していますが、実装はできるだけ一般的である(つまり、IDataReaderまたはDbDataReaderに適用される)ことが望ましいです。

答えて

5

私はジェイソンのソリューションに似た何かを示唆するが、そう、代わりのIDataReaderを実装してラッパーを使用します:

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

注意これは、すべての影響を受けずにプロパティのパススルーコードのかなりのビットを必要としないこと、利点は、後続の「読み込み」操作で前進することなく、結果セット内の任意の位置で「覗く」ことができる一般的な抽象であることです。

使用するには:

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

注意を前回の動作も「PEEK(」)でなかった場合に、すべての「のぞき見()」の呼び出しが実際に次のレコードに移動しますけれども。 'Read()'操作でこの対称性を保つことで、実装が簡単になり、より洗練されたAPIが提供されます。

+1

'IDataReader'インターフェース自体に' .Peek'メソッドが含まれていないので、あなたの例はうまくいきません。あなたは明示的に 'PeekDataReader'としてスコープ変数をタイプするか、' var'を使うべきです。 – julealgon

3

Peek()メソッドは必要ありません。 Do Whileループで必要なものを達成することができます。

ので、代わりの

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

あなたが

dr.read(); 
... do stuff 

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

Peek()は、同じDataReaderで複数の場所で複数回実行することができるため、このアプローチよりもはるかに柔軟です。どちらの場合でも「最初の」結果を渡す必要はありません。 –

+1

DataReaderの値を読み取ってもそれが先に移動されるわけではなく、Read()メソッドによって読み取られます。値を先に移動させずに、何度でも値を読み取ることができます。 Peek()がどのように値を追加するかはわかりません。私はあなたのポイントを見逃しているかもしれません。 ???? –

+0

これは実行可能ですが、私は手動ではなくlinq経由で読者を使いたいと思っていました。 –

4

あなたは、通常モードVS PEEK-モードを追跡するステートマシンを作成することができます。たぶん、このような何か(ちょうどPeeker.csまたはそのような何かと呼ばれる単一のファイルにそれらのすべてを投げることができ):やり過ぎの

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

種類が、まあいい。

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

は、その後、あなたの行に何をする必要があるかの操作を行います。あなただけの次んでしょう、それを使用するには

。オブジェクト配列を使用するよりも良い方法があるかもしれませんが、その点が分かります。

幸運を祈る!

+0

これは多かれ少なかれ私が進めていたものです。状態情報を保持できるように、ラッパークラスが必要なように見えます。しかし、それは面倒の価値があるかどうかは分かりません。 –

+0

それがどれほど重要かによって異なります。それを行うにはあまりにも多くのコードを取ることはありませんが、それは少し仕事がかかるでしょう(3つの小さなクラスがありそうです)。 –

+0

あの4つの小さなクラス=) –

関連する問題