2009-05-21 99 views
6

在ado.net的DataReader上似乎没有Peek方法。我希望能够在通过我的阅读器进行循环之前执行一些一次性处理,并且能够查看第一行中的数据而不会导致其在随后的迭代中被跳过,这会很好。什么是完成这个最好的方法?如何在DataReader上实现Peek()函数?

我使用的是SqlDataReader,但最好是实施尽可能通用(即适用于IDataReaderDbDataReader)。

回答

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。

3

您不需要Peek()方法。您可以使用Do While循环完成您的需求。

所以不是

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

你会

dr.read(); 
... do stuff 

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

Peek()比这种方法更灵活,因为它可以在同一个DataReader上多次完成,而且不必在任何地方传递“第一次”结果。 – 2009-05-21 17:53:42

+1

读取DataReader值不会将其向前移动,Read()方法会执行此操作。您可以根据需要多次读取这些值,而无需将其向前移动。我不确定Peek()会如何增加任何值。我可能错过了你的观点。 ???? – 2009-05-21 17:56:54

+0

这是可行的,但我想通过linq而不是手动使用读取器。 – 2009-05-21 18:06:33

4

您可以创建一个状态机跟踪窥视模式VS常规模式。也许这样的事情(可能只是折腾他们都到名为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); 

然后你需要与你的行做什么。有可能比使用对象数组更好,但你明白了。

祝你好运!