Почему ленивая загрузка приводит к загрузке свойства навигации класса?

#c# #entity-framework #entity-framework-6 #lazy-loading

Вопрос:

В EF, пытаясь понять, что такое ленивая загрузка, отметили навигацию по зачислениям как виртуальную.

 class Program {

   static void Main(string[] args) {

      using (var context = new UniContextEntities()) {

         //Loading students only
         IList<Student> students = context.Students.ToList<Student>();

         foreach (var student in students) {

            string name = student.FirstMidName   " "   student.LastName;
            Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
    
            foreach (var enrollment in student.Enrollments) {
               Console.WriteLine("Enrollment ID: {0}, Course ID: {1}", 
                  enrollment.EnrollmentID, enrollment.CourseID);
            }
         }

         Console.ReadKey();
      }
   }
}
 

Результат:

 ID: 1, Name: Ali Alexander
       Enrollment ID: 1, Course ID: 1050
       Enrollment ID: 2, Course ID: 4022
       Enrollment ID: 3, Course ID: 4041
ID: 2, Name: Meredith Alonso
       Enrollment ID: 4, Course ID: 1045
       Enrollment ID: 5, Course ID: 3141
       Enrollment ID: 6, Course ID: 2021
ID: 3, Name: Arturo Anand
       Enrollment ID: 7, Course ID: 1050
 

но если свойство помечено как невиртуальное, следовательно, оно не является ленивым или отключено в конфигурации, то вывод будет.

 ID: 1, Name: Ali Alexander
ID: 2, Name: Meredith Alons
ID: 3, Name: Arturo Anand
 

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

Комментарии:

1. Взгляните на различные способы загрузки связанных данных . Вам нужно явно загрузить его, если не ленивая загрузка. Будь то «Нетерпеливая загрузка» или «Явная загрузка».

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

Ответ №1:

Отключение ленивой загрузки не означает, что она будет загружаться с нетерпением, это означает, что она просто не будет загружаться с задержкой, оставляя ссылки #null, если только DbContext их уже не загрузил. Если вы знаете, что вам нужны записи, когда вы загружаете студента, то загружайте их с нетерпением Include , независимо от того, включена ли отложенная загрузка или нет.

Чтобы обозначить разницу:

С ленивой загрузкой:

 // This line executes SQL like "SELECT * FROM dbo.Students"
IList<Student> students = context.Students.ToList<Student>();

foreach (var student in students) {

    string name = student.FirstMidName   " "   student.LastName;
    Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);
        
    // This line will execute SQL line "SELECT * FROM dbo.Enrollments WHERE StudentId = {student.StudentId}
    foreach (var enrollment in student.Enrollments) {
        // ....
    }
}
 

Vs. Нетерпеливая загрузка:

 // This line executes SQL like "SELECT * FROM dbo.Students s INNER JOIN dbo.Enrollments e ON e.StudentId = s.StudentId"
IList<Student> students = context.Students.Include(x => x.Enrollments).ToList<Student>();

foreach (var student in students) {

    string name = student.FirstMidName   " "   student.LastName;
    Console.WriteLine("ID: {0}, Name: {1}", student.ID, name);

    // This line no longer triggers any DB call.        
    foreach (var enrollment in student.Enrollments) {
    // ....
    }
}
 

Разница в производительности может быть весьма существенной. Если бы вы загрузили 100 студентов и их зачисление, ленивая загрузка привела бы к отправке 101 запроса в базу данных и ожиданию. Один для студентов, затем для каждого студента, еще один для зачисленных. Если бы у студента было 4 или 5 ссылок, это могло бы быть 401-501 запросом. При активной загрузке это всегда будет один запрос. Для больших объектов с большим количеством ссылок или большим количеством возвращаемых записей это все равно может легко привести к ошибкам в производительности, которых следует опасаться.