使用IDataReader作为IEnumerable <t>的最佳方法?</t>

时间:2011-08-04 09:31:08

标签: 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();
        IEnumerable<SqlDataReader> sqlEnumerable = sqlDataReader.AsEnumerable();
        var c = sqlEnumerable.Count();
        var s = sqlEnumerable.Sum();
        SqlDataReader first = sqlEnumerable.First();
        var t = first.GetSqlXml(10);
    }
}

写这个的最好方法是什么。 请写下你的片段。

6 个答案:

答案 0 :(得分:14)

您可以使用:

MyDataReader.Cast<IDataRecord>()

但是在关闭DataReader之前不要忘记执行linq语句。
使用ToList()例如

答案 1 :(得分:8)

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

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

在此处找到:http://www.thinqlinq.com/default/Consuming-a-DataReader-with-LINQ.aspx


正如@LukeH指出的那样,请注意,由于IDataReader仅支持一次转发,因此您只能查询一次可枚举数。 (为了解决这个问题,你可以调用ToList / ToArray,然后查询)。

请注意SqlDataReader已经暗示IEnumerable,因此您不需要在您给出的示例中执行此操作。

另外,请注意,在服务器上进行任何过滤/聚合可能更好(例如,通过LINQ to SQL

答案 2 :(得分:8)

试试,这个:

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);
    }
}

答案 3 :(得分:5)

这是我的两分钱:

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

我觉得发布这个的冲动,因为在使用后处置DataReader非常重要,没有回答提到它。

这就是我的实现在using循环周围有while语句的原因。通过这种方式,我可以“一手”查询,而不用担心DataReader处置。

答案 4 :(得分:3)

您只需将DataReader加载到DataTable然后加载Select()

DataTable dt = new DataTable();
dt.Load(dataReader);
DataRow[] rows = dt.Select();        //DataRow[] Implements IEnumerable

IEnumerable<DataRow> rows = dt.AsEnumerable();

答案 5 :(得分:0)

我已经使用了以下内容,但我更喜欢@ Serge的建议,它让人想起我以前做过的事情,但忘记了IDataReader的实现

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