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

问题描述 投票:0回答:7

我需要在任何像这样的 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);
    }
}

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

c# .net extension-methods ienumerable
7个回答
19
投票

您可以使用这个:

MyDataReader.Cast<IDataRecord>()

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


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

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


7
投票

这是我的两分钱:

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

public void Test()
{
  using MyDataReader = GetDataReader();
  var Res =
    from Dr in MyDataReader.Enumerate()
    select new {
      ID = (Guid)Dr["ID"],
      Description = Dr["Desc"] as string
    };
}

我很想发帖,因为使用后处理

DataReader
非常重要,但没有答案提到它。

这就是为什么我的实现在

using
循环周围有一个
while
语句。通过这种方式,我可以进行“一只手”查询,而不必担心
DataReader
处置。


6
投票

您可以将

DataReader
加载到
DataTable
中,然后
Select()
:

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

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

0
投票

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

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

0
投票

今天我实现了以下通用解决方案,因为我们不喜欢依赖任何第三方依赖项或任何复杂的解决方案。

使用反射 + lambda 表达式进行

简单快速的

IDataReader
IEnumerable<T>
转换

public IEnumerable<T> ConvertToEnumerable<T>(SqlDataReader dr) where T : class, new()
{
    List<string> lstColumns = Enumerable.Range(0, dr.FieldCount).Select(dr.GetName).ToList();
    List<PropertyInfo> lstProperties = typeof(T).GetProperties().Where(x => lstColumns.Contains(x.Name, StringComparer.OrdinalIgnoreCase)).ToList();
    while (dr.Read())
    {
        var entity = new T();
        lstProperties.Where(w => dr[w.Name] != System.DBNull.Value).ToList().ForEach(i => i.SetValue(entity, dr[i.Name], null));
        yield return entity;
    }
}

用途

SqlDataReader dr;
...
var result = ConvertToEnumerable<Foo>(dr).FirstOrDefault();
Or
var result = ConvertToEnumerable<Foo>(dr).ToList();
© www.soinside.com 2019 - 2024. All rights reserved.