在ado.net的DataReader上似乎没有Peek
方法。我希望能够在通过我的阅读器进行循环之前执行一些一次性处理,并且能够查看第一行中的数据而不会导致其在随后的迭代中被跳过,这会很好。什么是完成这个最好的方法?如何在DataReader上实现Peek()函数?
我使用的是SqlDataReader
,但最好是实施尽可能通用(即适用于IDataReader
或DbDataReader
)。
在ado.net的DataReader上似乎没有Peek
方法。我希望能够在通过我的阅读器进行循环之前执行一些一次性处理,并且能够查看第一行中的数据而不会导致其在随后的迭代中被跳过,这会很好。什么是完成这个最好的方法?如何在DataReader上实现Peek()函数?
我使用的是SqlDataReader
,但最好是实施尽可能通用(即适用于IDataReader
或DbDataReader
)。
我建议类似杰森的解决方案的东西,但使用实现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。
您不需要Peek()方法。您可以使用Do While循环完成您的需求。
所以不是
while(dr.read())
{
... do stuff
}
你会
dr.read();
... do stuff
do
{
... do stuff
}while(dr.read())
您可以创建一个状态机跟踪窥视模式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);
然后你需要与你的行做什么。有可能比使用对象数组更好,但你明白了。
祝你好运!
Peek()比这种方法更灵活,因为它可以在同一个DataReader上多次完成,而且不必在任何地方传递“第一次”结果。 – 2009-05-21 17:53:42
读取DataReader值不会将其向前移动,Read()方法会执行此操作。您可以根据需要多次读取这些值,而无需将其向前移动。我不确定Peek()会如何增加任何值。我可能错过了你的观点。 ???? – 2009-05-21 17:56:54
这是可行的,但我想通过linq而不是手动使用读取器。 – 2009-05-21 18:06:33