Лучший способ использовать IDataReader как IEnumerable <T>? - PullRequest
4 голосов
/ 04 августа 2011

Мне нужно использовать Linq на любых IDataReader подобных реализациях

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 ]

14 голосов
/ 11 февраля 2014

Вы можете использовать это:

MyDataReader.Cast<IDataRecord>()

Но не забудьте выполнить оператор linq до закрытия DataReaderиспользуя ToList (), например,

8 голосов
/ 04 августа 2011

Попробуйте, это:

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 голосов
/ 04 августа 2011

Вы можете создать метод расширения для этого (см. Предостережения ниже) :

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 )

5 голосов
/ 28 ноября 2015

Вот мои два цента:

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.

3 голосов
/ 10 ноября 2015

Вы можете просто загрузить DataReader в DataTable, а затем Select():

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

или

IEnumerable<DataRow> rows = dt.AsEnumerable();
0 голосов
/ 17 января 2017

Я использовал следующее, но мне больше нравится предложение Сержа, и оно напоминает то, что я делал раньше, но потом забыл, что IDataReader реализует

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