Search code examples
c#linq

Entity Framework: select records that don't exist in relationship


I'm struggling to get a list of students that are missing required courses.

I have a many to many relation using a joining table:

Student:

public class Student: IBaseEntity
{
    [Key]
    public Guid ID { get; set; }

    private string _name = string.Empty;
    public string Name
    {
        get => _name;
        set => _name = value!.ToUpper(CultureInfo.InvariantCulture);
    }
    // removed additional fields for brevity
}

Course:

public class Course: IBaseEntity
{
    [Key]
    public Guid ID { get; set; }

    private string _name = string.Empty;
    public string Name
    {
        get => _name;
        set => _name = value!.ToUpper(CultureInfo.InvariantCulture);
    }

    public bool Required { get; set; }

    // removed additional fields for brevity
}

Action:

public class Action: IBaseEntity
{
    [Key]
    public Guid ID { get; set; }

    public Student Student { get; set; }

    public Course Course { get; set; }
    
    // the date that the student took the course
    public Datetime AttendedDate { get; set; }

    // removed additional fields for brevity
}

How to get a list of students that are missing required courses?

For instance as a Dictionary<string, Student>, where string is the course and the student that didn't take the course.

I tried a foreach(var s in Students) and inside a foreach(var c in courses), but it hangs...

Dictionary<string, Student> dict = new();

foreach (var s in Students)
{
    foreach (var c in Courses)
    {
        bool exists = actions.Any(x => x.Student.ID.Equals(c.ID) && x.Course.ID.Equals(c.ID));

        if (!exists)
        {
            dict.Add(r.Name, c);
        }
    }
}

Maybe I'm doing the relationship in a wrong way?

Any help is appreciated.

PS: sorry about my English, not my first language.


Solution

  • You must test x.Student.ID.Equals(s.ID) not c.ID.

    And you cannot add duplicate keys to a dictionary. E.g., add the s and missing c to a List<(Student student, Course course)> instead, where the list contains tuples.

    List<(Student student, Course course)> missing = new();
    foreach (var s in Students) {
        foreach (var c in Courses) {
            bool exists = actions.Any(a => a.Student.ID == s.ID && a.Course.ID == c.ID);
            if (!exists) {
                missing.Add((s, c));
            }
        }
    }
    
    foreach (var (student, course) in missing) {
        Console.WriteLine($"{student.Name} did not attend \"{course.Name}\"");
    }