简体   繁体   中英

How to make this query work on Entity Framework Code First CTP5

I have the following model (dumbed down for this question):

public class Student
{
    public int ID { get; set; }
    public virtual ICollection<StudentCourse> StudentCourses { get; set; }

     public Student(){
        StudentCourses = new List<StudentCourse>();
    }
}

public class StudentCourse
{
    public int ID { get; set; }
    public string Grade { get; set; }
    public int StudentID { get; set; }
    public int CourseID { get; set; }

    public virtual Student Student { get; set; }
    public virtual Course Course { get; set; }
}

public class Course
{
    public int ID { get; set; }        
    public virtual ICollection<StudentCourse> StudentCourses { get; set; }

    public Course() {
        StudentCourses = new List<StudentCourse>();
    }
}

Everything is linked in my DbContext class and working as expected. I want to find all the students that have taken part in a course so I do:

var query = db.Students
            .Where(s => s.StudentCourses.Course.ID == 11); 

but it fails because for some reason EF can't see the link between StudentCourse and Course. The error message is:

'System.Collections.Generic.ICollection' does not contain a definition for 'Course' and no extension method 'Course' accepting a first argument of type 'System.Collections.Generic.ICollection' could be found (are you missing a using directive or an assembly reference?).

However the following works fine:

var query = db.StudentCourses
            .Where(s => s.Course.ID == 11)
            .Select(s=>s.Students);

Is this a bug or am I doing something wrong?

StudentCourses is a list of Courses, you can't access a single course on it with .Course. In order to do that, you need to use a clause which works on individual records, such as select, where, any, etc. (most of the IEnumerable extensions taking a lambda expression do so for this purpose.)

var query = db.Students.Where(s => s.StudentCourses.Any(r => r.Course.CourseID == 11)); 

If you want to work off of db.Students, fine, but the second example you posted is probably more semantically close to what you're doing. StudentCourses represents the connection between students and courses. Your where clause restricts to a specific course, the select clause projects a student course collection over its connected student.

Why is this part in your code:

public Student(){
    StudentCourses = new List<StudentCourse>();
}

I don't need to include that part when I do it, it works fine just having

public virtual ICollection<StudentCourse> StudentCourses { get; set; }

all by itself. You are preventing internal preparation stuff from working by assigning a list in advance.


Please note that

var query = db.Students
              .Where(s => s.StudentCourses.Course.ID == 11);

won't necessarily work because StunderdCourses might be lazily evaluated, you will want to use:

var query = db.Students
              .Include("StudentCourses")
              .Where(s => s.StudentCourses.Course.ID == 11);

Which will make sure that for each student its StudentCourses are included in the query as well.


Note that the above query is inefficient as you are fetching all students and for each student you are fetching all his courses, the code you have suggested at the end is much better as it looks for the right courses first and then find the students that belong to it:

var query = db.StudentCourses
              .Include("Course")
              .Where(s => s.Course.ID == 11)
              .Select(s=>s.Students);

Note the use of Include again, which is necessary on servers that do lazy evaluation.


But, note that the class actually has the CourseID property so you could also do:

var query = db.StudentCourses
              .Where(s => s.CourseID == 11)
              .Select(s=>s.Students);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM