EF загружает только отфильтрованные дочерние записи - PullRequest
0 голосов
/ 17 июня 2020

Допустим, у меня есть типичные студенты и классы курсов, извлеченные как сущности и автоматически созданные через EF6. Пример:

class Student {
      string name,
      virtual ICollection<Course> Courses
      virtual ICollection<A> example a
      virtual ICollection<B> example b
      virtual ICollection<C> example c
      etc...
}

class Course {
      DateTime courseDate,
      virtual Student Student
      etc...
}

Я хочу получить список студентов, у которых завтра курс, и у которых этот курс будет только в дочернем списке. (Предположим, не более 1 курса в день)

Вещи, которые я пробовал ....

List<Student> Method 1 (DateTime date)
{
    ctx.Configurations.LazyLoadingEnabled = false;
    return (from s in ctx.Students.Include(x=>x.Course)
                 join c in ctx.Courses
                 where c.courseDate == date
                 select s).ToList();
}

Результат: список студентов, прошедших курс (но ничего в объект курсов)

List<Student> Method 2 (DateTime date)
{
    return ctx.Students.Where(x=>x.Courses.Any(y=>y.courseDate==date)).ToList()
}

Результат: список студентов со всеми прикрепленными и неотфильтрованными свойствами.

List<Student> Method 3 (DateTime date)
{
    ctx.Configurations.LazyLoadingEnabled = false;
    return ctx.Students.Include(x=>x.Courses).Where(x=>x.Courses.Any(y=>y.courseDate==date)).ToList()
}

Результат: список студентов с прикрепленным свойством только курсов, но ЕЩЕ НЕфильтрованным.

    List<Student> Method 4 (DateTime date)
    {
        ctx.Configurations.LazyLoadingEnabled = false;
        return 
  ctx.Students.Include(x=>x.Courses).Where(x=>x.Courses.Select(y=>y.courseDate).Contains(date)).ToList()
    }

Результат: То же, что и выше

Список продолжается ....

Я не хочу, чтобы данные передавались и фильтровались после первоначального запроса.

Может кто поможет.

1 Ответ

0 голосов
/ 17 июня 2020

Итак, у вас есть студенты и курсы. Каждый студент посещает ноль или более курсов. Я думаю, что курсы посещают ноль или больше студентов (многие ко многим). Мне кажется, вы разработали так, чтобы каждый курс посещал ровно один студент (один-ко-многим).

Если вы действительно рассчитывали один-ко-многим, ваш курс курса должен немного измениться:

class Course
{
    public int Id {get; set;}
    public DateTime CourseDate {get; set;}
    ...

    public int StudentId {get; set;}   // <======= !!!
    public virtual Student Student {get; set;}
}

Вернемся к вашей проблеме

Вы написали:

Я хочу получить список студентов, у которых завтра курс и которые только что пройдут через список дочерних элементов.

Из ваших примеров кажется, что вы хотите, чтобы все студенты, у которых завтра будет хотя бы один курс, вместе со всеми курсами, которые этот студент будет завтра.

Обычно, когда с использованием структуры сущностей было бы достаточно использовать virtual ICollections, как вы:

DateTime tomorrow = ...
var studentsWithCoursesOnDate = dbContext.Students.Select(student => new
{
    // Select only the properties that you plan to use:
    Id = student.Id,
    Name = student.Name,
    ... 

    CoursesOnDate = student.Courses
        .Where(course => course.CourseDate == tomorrow)
        .Select(course => new
        {
            // again: only the properties that you plan to use
            Id = course.Id,
            Title = course.Title,
            ...

            // not needed, you know the value
            // StudentId = course.StudentId,
        })
        .ToList(),
})
.ToList();

Это должно работать с полной структурой сущностей. Entity framework знает отношения между таблицами и выполнит для вас правильный GroupJoin. Я слышал, что вы не можете использовать виртуальные ICollections в EF-core, я не проверял это.

Если вы хотите сделать GroupJoin самостоятельно:

DateTime tomorrow = ...
IQueryable<Course> tomorrowCourses = dbContext.Courses
    .Where(course => course.CourseDate == tomorrow);

var studentsWithCoursesOnDate = dbContext.Students
    .GroupJoin(tomorrowCourses,             // GroupJoin Students and tomorrow's Courses
    student => student.Id,                  // from every Student take the Id
    course => course.StudentId,             // from every Course take the foreign key

    (student, coursesOfThisStudent) => new  // when they match, make one new object
    {
         // again: select only the properties that you plan to use:
         Id = student.Id,
         Name = student.Name,
         ...

        CoursesOnDate = coursesOfThisStudent.Select(course => new
        {
            // again: only the properties that you plan to use
            Id = course.Id,
            Title = course.Title,
            ...
        })
        .ToList(),
})
.ToList();

Примечание: идентификатор coursesOfThisStudent на самом деле содержит только завтрашние курсы этого студента, но это сделало бы идентификатор излишне длинным.

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

При запросе данных всегда используйте Select для выбора нужных значений. Используйте «Включить», только если вы планируете изменить (обновить) полученные данные.

...