Вы должны понимать, что DbSet<Student>
не представляет вашу коллекцию Students
, она представляет таблицу Students
в вашей базе данных. Это означает, что вы можете запрашивать последовательности свойств Students
.
При желании вы можете запросить полную последовательность, но это приведет к проблемам с производительностью, если не к проблемам с памятью.
Поэтому, если вы запрашиваете данные Student
, вы должны иметь в виду, что вы будете использовать из полученных данных: не выбирайте свойства, для которых вы уже знаете значение, не выбирайте элементы, которые вы не планирую использовать.
Пример: база данных с Schools
и Students
, с отношением один-ко-многим, каждый School
имеет ноль или более Students
, каждый Student
посещает ровно один School
:
class School
{
public int Id {get; set;}
public string Name {get; set;}
...
// every School has zero or more Students (one-to-many)
public virtual ICollection<Student> Students {get; set;}
}
class Student
{
public int Id {get; set;}
public string Name {get; set;}
...
// Every Student attends exactly one School, using foreign key:
public int SchoolId {get; set;}
public virtual School School {get; set;}
}
В структуре сущностей столбцы таблиц представлены не виртуальными свойствами. Виртуальные свойства представляют отношения между таблицами (один ко многим, многие ко многим, ...)
Не делайте следующее!
public IEnumerable<School> GetSchoolByLocation(string city)
{
return mySchoolWithItsStudents = dbContext.Schools
.Where(school => school.City == city)
.Include(school => school.Students)
.ToList();
}
Почему бы и нет? Это похоже на идеальный код, не так ли?
Ну, может быть, вы получаете больше данных, чем будет использовать ваш абонент:
var mySchoolId = GetSchoolByLocation("Oxford")
.Where(school => schoolStreet == "Main Street")
.Select(school => school.Id)
.FirstOrDefault();
Какая трата, сначала забрать все оксфордские школы, а потом оставить только эту!
Кроме того: вы получаете школу со всеми ее учениками и все, что вы используете, если идентификатор школы?
Попробуйте вернуть IQueryable<...>
как можно дольше, и пусть вызывающая сторона решит, что делать с возвращенными данными.
Может быть, он хочет сделать ToList
, или Count
, или FirstOrDefault
. Может быть, он хочет только Id
и Name
. Пока вы этого не знаете, не принимайте решение за него, это только делает ваш код менее пригодным для повторного использования.
Всегда используйте Select
для выбора свойств и выбирайте только те данные, которые вы действительно планируете использовать. Используйте Include
только если вы планируете обновить включенные данные.
var schools = dbContext.Schools.Where(school => ...)
// Keep only the Schools that you actually plan to use:
.Select(school => new
{
// only select the properties that you plan to use
Id = school.Id,
Name = school.Name,
...
// Only the Students you plan to use:
Students = school.Students.Where(student => ...)
.Select(student => new
{
// Again, only the properties you plan to use
Id = student.Id,
Name = student.Name,
// no need for the foreign key: you already know the value
// SchoolId = student.SchoolId,
}),
});
Наконец, если вам нужен доступ ко всем Students
, чтобы показать их, но вы не хотите получать все миллионы студентов одновременно, подумайте о том, чтобы выбрать их по странице. Запомните первичный ключ последнего элемента последней выбранной страницы и используйте `.Where (item => item.Id> lastFetchedPrimaryKey) .Take (pageSize), чтобы получить следующую страницу, пока страниц больше не будет.
Таким образом, вы можете запросить 50 учеников, в то время как вы отобразите только 25 из них, но, по крайней мере, у вас не все миллионы учеников в памяти. Выборка следующей страницы выполняется довольно быстро, поскольку индекс первичного ключа уже существует, а извлеченные элементы упорядочены по первичному ключу.