Reputation: 243
For a school project I need to filter students who have signed up for multiple courses at the same timeblock. Instead of querying the DB via procedures/views I want to use LINQ to filter it in memory for learning purposes.
Everything seems alright according to the debugger however the result of my linq query is 0 and I can't figure out how.
Here's the code:
foreach (Timeblock tb in ctx.Timeblocks)
{
List<Student> doublestudents = new List<Student>();
//Get the schedules matching the timeblock.
Schedule[] schedules = (from sched in ctx.Schedules
where sched.Timeblock.Id == tb.Id
select sched).ToArray();
/\/\/\Gives me 2 schedules matching that timeblock.
if (schedules.Count() > 1)
{
doublestudents = (from s in ctx.Students
where s.Courses.Contains(schedules[0].Course) && s.Courses.Contains(schedules[1].Course)
select s).ToList();
Console.WriteLine(doublestudents.Count); <<< count results in 0 students.
}
}
While debugging it seems everything should work alright.
Each student has a List and each Course hsa a List
schedules[0].Course has Id 1 schedules[0].Course has Id 6
The student with Id 14 has both these courses in it's list.
Still the linq query does not return this student. Can this be because it's not the same reference of course it wont find a match at the .Contains()?
It's driving me totally crazy since every way I try this it wont return any results while there are matches...
Upvotes: 0
Views: 901
Reputation: 8292
You are comparing on Course
which is a reference type. This means the objects are pointing to locations in memory rather than the actual values of the Course
object itself, so you will never get a match because the courses of the student and the courses from the timeblock query are all held in different areas of memory.
You should instead use a value type for the comparison, like the course ID. Value types are the actual data itself so using something like int
(for integer) will let the actual numerical values be compared. Two different int
variables set to the same number will result in an equality.
You can also revise the comparison to accept any number of courses instead of just two so that it's much more flexible to use.
if (schedules.Count() > 1)
{
var scheduleCourseIds = schedules.Select(sch => sch.Course.Id).ToList();
doublestudents = (from s in ctx.Students
let studentCourseIds = s.Courses.Select(c => c.Id)
where !scheduleCourseIds.Except(studentCourseIds).Any()
select s).ToList();
Console.WriteLine(doublestudents.Count);
}
Some notes:
let
keyword in Linq to create temporary variables you can use in the query and make everything more readable.Upvotes: 1
Reputation: 27861
As you have guessed, this is probably related to reference equality. Here is a quick fix:
doublestudents =
(from s in ctx.Students
where s.Courses.Any(c => c.Id == schedules[0].Course.Id) &&
s.Courses.Any(c => c.Id == schedules[1].Course.Id)
select s).ToList();
Please note that I am assuming that the Course
class has a property called Id
which is the primary key. Replace it as needed.
Please note that this code assumes that there are two schedules. You need to work on the code to make it work for any number of schedules.
Another approach is to override the Equals
and GetHashCode
methods on the Course
class so that objects of this type are compared based on their values (the values of their properties, possibly the ID property alone?).
Upvotes: 0
Reputation: 136
The problem is that your schedule[0].Course object and the s.Courses, from the new query, are completely different.
you may use the element's key to evaluate your equality condition/expression, as:
if (schedules.Count() > 1)
{
doublestudents = (from s in ctx.Students
where s.Courses.Any(x=> x.Key == schedules[0].Course.Key) && s.Courses.Any(x=> x.Key == schedules[1].Course.Key)
select s).ToList();
Console.WriteLine(doublestudents.Count); <<< count results in 0 students.
}
}
In order to achieve this you will need to include
using System.Linq
Upvotes: 0