C# 将IDataReader用作IEnumerable的最佳方法<;T>;?

C# 将IDataReader用作IEnumerable的最佳方法<;T>;?,c#,.net,extension-methods,ienumerable,C#,.net,Extension Methods,Ienumerable,我需要在任何像这样的IDataReader实现上使用Linq var c = sqlDataReader.AsEnumerable().Count(); 例如: public abstract class Test { public abstract SqlDataReader GetSqlDataReader(); public void Foo() { SqlDataReader sqlDataReader = GetSqlDataReader()

我需要在任何像这样的IDataReader实现上使用Linq

var c = sqlDataReader.AsEnumerable().Count();
例如:

public abstract class Test
{
    public abstract SqlDataReader GetSqlDataReader();

    public void Foo()
    {
        SqlDataReader sqlDataReader = GetSqlDataReader();
        IEnumerable<SqlDataReader> sqlEnumerable = sqlDataReader.AsEnumerable();
        var c = sqlEnumerable.Count();
        var s = sqlEnumerable.Sum();
        SqlDataReader first = sqlEnumerable.First();
        var t = first.GetSqlXml(10);
    }
}
公共抽象类测试
{
公共抽象SqlDataReader GetSqlDataReader();
公共图书馆
{
SqlDataReader SqlDataReader=GetSqlDataReader();
IEnumerable sqlEnumerable=sqlDataReader.AsEnumerable();
var c=sqlEnumerable.Count();
var s=sqlEnumerable.Sum();
SqlDataReader first=sqlEnumerable.first();
var t=first.GetSqlXml(10);
}
}
写这篇文章的最好方法是什么。
请编写您的代码片段。

您可以创建一个扩展方法来执行此操作(请参阅下面的注意事项)

public static class DataReaderExtension
{
    public static IEnumerable<Object[]> AsEnumerable(this System.Data.IDataReader source)
    {
        if (source == null)
            throw new ArgumentNullException("source");

        while (source.Read())
        {
            Object[] row = new Object[source.FieldCount];
            source.GetValues(row);
            yield return row;
        }
    }
}
公共静态类DataReaderExtension
{
公共静态IEnumerable AsEnumerable(此System.Data.IDataReader源)
{
if(source==null)
抛出新的ArgumentNullException(“源”);
while(source.Read())
{
Object[]行=新对象[source.FieldCount];
source.GetValues(行);
收益返回行;
}
}
}
可在此处找到:



正如@LukeH所指出的,请注意,由于IDataReader只支持读取一次,所以只能查询一次可枚举项。(要解决这个问题,可以调用ToList/ToArray,然后查询该问题)

请注意,
SqlDataReader
已经实现了IEnumerable,因此在您给出的示例中不需要这样做

另外,请注意,最好在服务器上进行任何筛选/聚合(例如通过)

尝试以下操作:

public static class DataReaderExtension
{
    public class EnumeratorWrapper<T>
    {
        private readonly Func<bool> moveNext;
        private readonly Func<T> current;

        public EnumeratorWrapper(Func<bool> moveNext, Func<T> current)
        {
            this.moveNext = moveNext;
            this.current = current;
        }

        public EnumeratorWrapper<T> GetEnumerator()
        {
            return this;
        }

        public bool MoveNext()
        {
            return moveNext();
        }

        public T Current
        {
            get { return current(); }
        }
    }

    private static IEnumerable<T> BuildEnumerable<T>(
            Func<bool> moveNext, Func<T> current)
    {
        var po = new EnumeratorWrapper<T>(moveNext, current);
        foreach (var s in po)
            yield return s;
    }

    public static IEnumerable<T> AsEnumerable<T>(this T source) where T : IDataReader
    {
        return BuildEnumerable(source.Read, () => source);
    }
}
公共静态类DataReaderExtension
{
公共类枚举器包装器
{
私有只读Func moveNext;
私有只读函数当前;
公共枚举器包装器(Func moveNext,Func current)
{
this.moveNext=moveNext;
这个电流=电流;
}
公共枚举器包装器GetEnumerator()
{
归还这个;
}
公共图书馆
{
返回moveNext();
}
公共电流
{
获取{返回当前();}
}
}
私有静态IEnumerable BuildEnumerable(
Func moveNext,Func current)
{
var po=新的枚举器包装器(moveNext,current);
foreach(采购订单中的var s)
收益率;
}
公共静态IEnumerable AsEnumerable(此T源),其中T:IDataReader
{
返回BuildEnumerable(source.Read,()=>source);
}
}
您可以使用以下功能:

MyDataReader.Cast<IDataRecord>()
MyDataReader.Cast()
但是不要忘记在关闭DataReader之前执行linq语句。

例如,使用ToList()

您只需将
数据读取器
加载到
数据表
中,然后
选择()

IEnumerable rows=dt.AsEnumerable();

这是我的两分钱:

public static IEnumerable<T> Enumerate<T>(this T reader) where T: IDataReader 
{ 
   using(reader) 
      while(reader.Read()) 
         yield return reader; 
} 

public void Test()
{
  var Res =
    from Dr in MyDataReader.Enumerate()
    select new {
      ID = (Guid)Dr["ID"],
      Description = Dr["Desc"] as string
    };
}
公共静态IEnumerable枚举(此T读取器),其中T:IDataReader
{ 
使用(读卡器)
while(reader.Read())
回传阅读器;
} 
公开无效测试()
{
var Res=
从MyDataReader.Enumerate()中的Dr
选择新的{
ID=(Guid)Dr[“ID”],
Description=Dr[“Desc”]作为字符串
};
}
我迫不及待地想发布这篇文章,因为在使用后处理
DataReader
是非常重要的,没有人提到它


这就是为什么我的实现在
while
循环中使用
语句的原因。通过这种方式,我可以进行“单手”查询,而不必担心数据阅读器的处理。

我使用了以下方法,但我更喜欢@Serge的建议,它让我想起了我相信我曾经做过的事情,但后来忘了IDataReader实现了

var reader = command.ExecuteReader();
var records = Enumerable
    .Range(0, int.MaxValue)
    .TakeWhile(i => reader.Read())
    .Select(i => reader as IDataRecord);

请注意,这将不允许他们像示例中那样先进行
计数
,然后进行
求和
,然后进行
第一次
。(尽管要做到这一点,他们要么需要重新查询数据库三次,要么以某种方式缓冲结果,两者都有潜在的缺陷。)@George Duckett“请注意,SqlDataReader已经实现了IEnumerable,因此在您给出的示例中不需要这样做。”,是的,这是正确的,但我需要IEnumerable来使用示例方法“GetSqlXml”“IEnumerable AsEnumerable”,我需要IEnumerable“您只能查询可枚举的一次”我知道,但我可以使用“AsQueryable()“要做到这一点,@b2Lord,
IEnumerable
没有意义,因为您只有一个
SqlDataReader
,您没有枚举多个
SqlDataReader
。嗨,Gabriel。您似乎有两个帐户(,)。如果您希望链接它们,请按照下面的步骤进行操作,因为对于您的代码来说似乎没有问题,但是对于更复杂的表,可能会出现错误,从而停止迭代,并且永远不会处理读取器。我遇到了这个问题,我创建了一个“DisposableEnumerable”类,以便在Enumerate方法之外进行使用,并在出现问题时调用Dispose。+1用于提出关于
using
语句的建议以及关闭datareader的重要性。在某些环境中,DataReader实际上可以
锁定数据库、表、叶等。
public static IEnumerable<T> Enumerate<T>(this T reader) where T: IDataReader 
{ 
   using(reader) 
      while(reader.Read()) 
         yield return reader; 
} 

public void Test()
{
  var Res =
    from Dr in MyDataReader.Enumerate()
    select new {
      ID = (Guid)Dr["ID"],
      Description = Dr["Desc"] as string
    };
}
var reader = command.ExecuteReader();
var records = Enumerable
    .Range(0, int.MaxValue)
    .TakeWhile(i => reader.Read())
    .Select(i => reader as IDataRecord);