2009-05-21 20 views
6

ado.net'te DataReader'da Peek yöntemi görünmüyor. Okuyucumun içine girmeden önce bir defaya mahsus bir işlem yapabilmeyi isterim ve sonraki satırda atlanmasına neden olmadan, ilk satırdaki verilere bakabilmemiz güzel olurdu. Bunu başarmanın en iyi yolu nedir?DataReader'da bir Peek() işlevini nasıl uygularım?

SqlDataReader kullanıyorum, ancak tercihen uygulama olabildiğince genel olacaktır (yani IDataReader veya DbDataReader için geçerlidir).

cevap

5

yüzden, Jason'ın çözümüne benzer bir şey önermek, ancak bunun yerine IDataReader uygulayan bir sarmalayıcı kullanarak olacaktır: Bu tüm etkilenmeyen özellikleri için geçiş kodunu biraz gerektirirken

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

Not Ancak faydası, sonuç kümesindeki herhangi bir pozisyonda, sonraki 'okuma' işleminde ilerlemeden 'göz atabilecek' genel bir soyutlama olmasıdır.

kullanmak için:

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

Not her 'Peek()' çağrısı aslında bir sonraki kayda hareket edecek olsa önceki operasyonu aynı zamanda bir 'Peek()' olmasaydı. Bu simetriyi 'Read()' işlemi ile sürdürmek daha basit bir uygulama ve daha zarif bir API sağlar.

+1

Örneğiniz, 'IDataReader' arabiriminin kendisi '.Peek 'yöntemini içermediği için çalışmaz. Açık alan değişkenini "PeekDataReader" veya "var" olarak kullanmanız gerekir. – julealgon

3

Peek() yöntemine ihtiyacınız yoktur. Bir Do While döngüsü ile ihtiyacınız olan şeyi başarabilirsiniz.

Yani yerine

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

Sen

dr.read(); 
... do stuff 

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

Peek(), bu yaklaşımdan biraz daha esnektir, çünkü aynı DataReader'da, birçok yerde, "ilk kez" sonuçlarının her ikisini de geçmek zorunda kalmadan, birden çok kez yapılabilir. –

+1

DataReader değerlerini okumak ileriye taşımaz, Read() yöntemi bunu yapar. Değerleri ileriye taşımadan istediğiniz kadar okuyabilirsiniz. Peek() 'ın nasıl bir değer kattığından emin değilim. Senin noktanı kaçırmış olabilirim. ???? –

+0

Bu uygulanabilir, ancak okuyucuyu el ile değil linq üzerinden kullanmak istiyordum. –

4

Düzenli-mode vs gözetleme-mode izleyen bir devlet makinesini oluşturabilir olacaktır. Belki böyle bir şey (sadece böyle tek Peeker.cs denilen dosya veya içine bir şey hepsini atmak olabilir): overkill

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, ama oh well.

yalnızca takip yapacağını kullanmak için:
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); 

Sonra satırla yapmanız gereken şeyi yapın. Bir nesne dizisi kullanmanın daha iyi bir yolu olabilir, ancak noktayı alırsınız.

İyi şanslar!

+0

Bu benim için gittiğim az çok şey. Durum bilgisini saklayabilmem için bir sarmalayıcı sınıfına ihtiyacım var gibi görünüyor. Yine de güçlük çekip değmediğinden emin değilim. –

+0

Ne kadar önemli olduğuna bağlı. Bunu yapmak için çok fazla kod gerektirmezdi, ama biraz iş gerektirecekti (3 küçük sınıf). –

+0

Bunu 4 küçük sınıfta yapın =) –

İlgili konular