Действительно ли AsQueryable () на ICollection делает ленивое выполнение? - PullRequest
8 голосов
/ 08 февраля 2012

Я использую Entity Framework CodeFirst, где я использовал отношения родитель-потомок, используя ICollection в качестве

public class Person
{
   public string UserName { get;set}
   public ICollection<Blog> Blogs { get; set;}
}

public class Blog
{
   public int id { get; set; }
   public string Subject { get; set; }
   public string Body { get; set; }
}

Хорошо, пока все работает хорошо, но меня беспокоит то, что когда я хочу получить Блоги человека, я получаю это как

var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var theBlogs = thePerson.Blogs.OrderBy(id).Take(5);

Теперь я понимаю, что когда строка выполняется, все блоги этого человека загружаются в память, а затем сортировка и выбор производятся из памяти. Это не идеально для записи человека, который имеет большое количество блогов. Я хочу сделать Blog Child дочерним для IQueryable, чтобы сортировка и выбор выполнялись в базе данных SQL перед извлечением в память.

Я знаю, что мог бы объявить блоги как IQueryable в моем контексте, чтобы я мог напрямую запросить как

var theBlogs = _context.Blogs.Where(.....)

но это неосуществимо для меня из-за выбора дизайна, я хочу избежать как можно большего количества циклических ссылок из-за проблемы сериализации. Итак, я не делал никаких ссылок на родительскую сущность в моем ребенке.

Я обнаружил, что я могу вызывать метод AsQueryable () в блогах как

var theBlogs = thePerson.Blogs.AsQueryable().OrderBy(id).Take(5);

Это похоже на волшебство для меня и кажется слишком хорошим, чтобы быть правдой. Итак, мой вопрос. Делает ли этот AsQueryable действительно ICollection как IQueryable в реальности и делает все процессы запросов в SQL Server (отложенная загрузка) ИЛИ это просто преобразование, при котором блоги загружаются в память, как и раньше, но меняют интерфейс с ICollection на IQueryable?

Ответы [ 2 ]

6 голосов
/ 08 февраля 2012

Так что на самом деле кажется, что записать ваше свойство навигации как IQueryable<T> невозможно .

Что вы можете сделать, это добавить свойство навигации к Blog:

public class Blog
{
   public int id { get; set; }
   public string Subject { get; set; }
   public string Body { get; set; }
   public virtual Person Owner { get; set; }
}

После этого вы можете выполнить запрос следующим образом, чтобы он не загружал все в память:

var thePerson = _context.Persons.Where(x => x.UserName = 'xxx').SingleOrDefault();
var results = _context.Blogs.Where(z => z.Person.Name = thePerson.Name).OrderBy(id).Take(5)

Я предлагаю вам попробовать LINQPad , чтобы увидеть, как LINQ переводится в SQL и что на самом деле запрашивается из БД.

2 голосов
/ 09 февраля 2012

Лучший подход описан в ответ Ладислава .В вашем случае:

var theBlogs = _context.Entry(thePerson)
                       .Collection(x => x.Blogs)
                       .Query()
                       .OrderBy(x => x.id)
                       .Take(5);
...