AsEnumerable и как он влияет на доходность и SqlDataReader - PullRequest
0 голосов
/ 06 марта 2012

Я пытаюсь понять, какое влияние оказывает AsEnumerable() на мои данные при итерации по ним. У меня есть фиктивный список в памяти. Если я foreach завершу первый вызов ToList(), это вызовет оценку, и моя распечатка будет выглядеть следующим образом (см. Код внизу этого поста, чтобы объяснить вывод):

entering da
yield return
yield return
yield return
exiting da
doing something to aaron
doing something to jeremy
doing something to brendan

Все имеет смысл. ToList() заставляет выходы в хранилище сначала выполняться в списке, затем мы получаем нашу итерацию foreach. Пока все хорошо.

Когда я делаю то же самое, за исключением использования AsEnumerable(), основываясь на том, что я прочитал относительно IQueryable (я понимаю, что это не IQueryable), я бы подумал, что это также вызывает оценку, но это не так , Это выглядит так:

entering da
yield return
doing something to aaron
yield return
doing something to jeremy
yield return
doing something to brendan
exiting da

Как если бы я даже не позвонил AsEnumerable(), поэтому мой вопрос:

  1. Почему AsEnumerable ведет себя иначе для коллекции в памяти по сравнению с linq to sql и типом возврата IQueryable?

  2. Как все это изменится, когда мой репозиторий изменится на использование SqlDataReader и выполнение yield return внутри считывателя (при вызове Read() метода). Будут ли строки, поступающие из SqlServer, которые буферизованы в сетевом буфере клиента, полностью проанализированы перед выполнением foreach (обычно здесь yield вызывает «паузу» в репо, пока каждая строка обрабатывается блоком foreach Я знаю, что если в этом случае я сначала позвоню ToList(), я смогу принудительно оценить SqlDataReader, поэтому делает ли AsEnumerable то же самое здесь?

Примечание: меня не интересует, хорошая ли идея ввода yield в SqlDataReader, поскольку он может держать соединение открытым, я уже забил эту тему до смерти:)

Вот мой тестовый код:

    public class TestClient
    {
        public void Execute()
        {
            var data = MockRepo.GetData();

            foreach (var p in data.AsEnumerable()) //or .ToList()
            {
                Console.WriteLine("doing something to {0}", p.Name);
            }

            Console.ReadKey();
        }
    }

    public class Person
    {
        public Person(string name)
        {
            Name = name;
        }

        public string Name { get; set; }
    }

    public class MockRepo
    {
        private static readonly List<Person> items = new List<Person>(3)
                        {
                            new Person("aaron"),
                            new Person("jeremy"), 
                            new Person("brendan")
                        };

        public static IEnumerable<Person> GetData()
        {
            Console.WriteLine("entering da");

            var enumerator = items.GetEnumerator();
            while (enumerator.MoveNext())
            {
                Console.WriteLine("yield return");
                yield return enumerator.Current;
            }

            Console.WriteLine("exiting da");
        }
    }

Ответы [ 3 ]

4 голосов
/ 06 марта 2012

AsEnumerable не делает ничего , кроме изменения типа выражения на IEnumerable<T>.Когда он используется в запросе, подобном следующему:

var query = db.Customers
              .Where(x => x.Foo)
              .AsEnumerable()
              .Where(x => x.Bar);

... это просто означает, что вы будете использовать Queryable.Where для первого предиката (то есть он преобразован в SQL) и Enumerable.Where для второгопредикат (поэтому он выполняется в вашем .NET-коде).

Он не требует оценки. ничего не делает .Он даже не проверяет, вызывается ли он null.

См. Мой пост Edulinq в блоге по AsEnumerable для получения дополнительной информации.

1 голос
/ 06 марта 2012

@ Джон Скит уже опубликовал, что делает AsEnumerable() - он просто меняет тип времени компиляции.Но зачем вам его использовать?

По сути, изменив выражение с IQueryable на IEnumerable, теперь вы можете использовать Linq to Objects (вместо реализации IQueryable вашим поставщиком базы данных) без каких-либо ограничений -не обязательно должен быть эквивалентный метод на стороне базы данных, поэтому вы можете свободно выполнять преобразование объекта, удаленные вызовы (если требуется) или любые виды манипуляций со строками.

При этом вы захотите сделать всефильтровать можно, пока вы еще работаете с базой данных (IQueryable) - в противном случае вы перенесете все эти строки в память, что будет стоить вам - и только тогда используйте AsEnumerable(), чтобы потом выполнить ваши окончательные преобразования.

0 голосов
/ 06 марта 2012

Согласно документации MSDN :

Метод AsEnumerable (Of TSource) (IEnumerable (Of TSource)) не имеет никакого другого эффекта, кроме изменения типа времени компиляцииисточника из типа, который реализует IEnumerable (Of T) для самого IEnumerable (Of T).

Это не должно вызывать никакой оценки, только намек на то, что вы хотите использовать методы IEnumerable по сравнению с какой-либо другой реализацией(IQueryable и т. Д.).

...