2009-05-21 6 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... 
    } 
} 

참고 그 모든 '픽() 호출은 실제로 다음 레코드로 이동합니다하지만 이전 작업은 또한'픽() '가 아니었다면. 'Read()'연산을 통해이 대칭을 유지하면 구현이 간단하고 고급 API가 제공됩니다.

+1

'IDataReader' 인터페이스 자체에'.Peek' 메소드가 없으므로 예제가 작동하지 않습니다. 명시 적으로 scope 변수를'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

당신은 일반 모드 대 들여다 모드를 추적하는 상태 머신을 만들 수있다. 어쩌면 이런 일이 (단지처럼 하나의 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

네 개의 작은 클래스들 =) –

관련 문제