1

this question has been asked several times on stackoverflow, and I have read through atleast half a dozen of those, but I can't get my head around a simple many to many linq join query. This is my database EDMXenter image description here

I'm simply trying to populate a WPF datagrid with the list of students, along with each student's subjects.

Now I know we can simply use navigation properties , instead of doing a join, but I have been unable to get the right result

so a ( either C#/VB.net )query like

var listOfStudents= // get a list with all students , along with each student's subjects 

Thank you for any help, its such a simple query but I'm kind of stuck

1
  • Why don't you just add another property in your Student model that gets his subjects so that the subjects will load automatically? Commented Jul 20, 2013 at 20:42

3 Answers 3

4
var listOfStudents = db.Student.Select(x => new { Id = x.Id, Name = x.StudentName, Subjects = x.StudentsSubjects.Select(y => y.Subject) }); 
Sign up to request clarification or add additional context in comments.

3 Comments

thx...unfortunately this only gives me a list of student names along with their respective ids, it doesn't fetch the subjects :(
@iAteABug_And_iLiked_it Strange, I just tested it and worked fine for me.
i'll spend some more time on this project tomorrow to try to get it goingn using the solution above or your solution...thx
2

If you remove Id field from the table StudentsSubject then remove this table from the model and update your model, EF will automatically convert this table in two navigational properties Subjects and Students for Student and Subject entities respectively.

If you must leave StudentsSubject table schema intact you can use Include() method to get both students and their subjects:

var students = dbContext.Students.Include("StudentsSubject.Subject") 

With 'normal' navigation properties you could write the following:

var students = dbContext.Students.Include("Subjects") 

Sometimes you need to assembly large graphs then Include() and lazy loading can affect performance. There is a small trick for this case:

// switch off for performance DbContext.Configuration.AutodetectChangesEnabled = false; // load root entities var roots = dbContext.Parents.Where( root => %root_condition%).ToList(); // load entities one level lower dbContext.DependentEntities.Where( dependent => dependent.Root%root_condition% && %dependent_condition%).ToList(); // detect changes dbContext.ChangeTracker.DetectChanges(); // enable changes detection. DbContext.Configuration.AutodetectChangesEnabled = true; 

Now root.Dependents collections are filled for roots.

Here is the tradeoff between join redundancy (include or join) and several db requests along with the increasing complexity of requests.

With 'includes' data for joined nodes is duplicated so a chain of includes can produce enormous traffic from DB to client.
With the second approach each level requires filtering conditions of all upper levels in Where() and EF generates the query with N-1 joins for the N-th level, but there is no place for redundancy.

As far as I know EF now works fine with Contains() and conditions for parent nodes can be substituted with Contains():

// load root entities var roots = dbContext.Parents.Where( root => %root_condition%).ToList(); var rootIds = new List<int>( roots.Select( root => root.Id)); // load entities one level lower dbContext.DependentEntities.Where( dependent => %dependent_condition% && rootIds.Contains( dependent.RootId)).ToList(); 

Comments

1

A regular LINQ join should do the trick. Here's a reduced test case:

Public Class Student Public Property Id As String Public Property StudentName As String Public Property GPA As String End Class Public Class StudentsSubject Public Property SubjectId As String Public Property StudentId As String Public Property Id As String End Class Public Class Subject Public Property Id As String Public Property SubjectName As String End Class Sub Main() Dim students As New List(Of Student) students.Add(New Student With {.Id = "1", .GPA = "GPA1", .StudentName = "John"}) students.Add(New Student With {.Id = "2", .GPA = "GPA2", .StudentName = "Peter"}) Dim subjects As New List(Of Subject) subjects.Add(New Subject With {.Id = "100", .SubjectName = "Maths"}) subjects.Add(New Subject With {.Id = "200", .SubjectName = "Physics"}) Dim studentsSubject As New List(Of StudentsSubject) studentsSubject.Add(New StudentsSubject With {.Id = "10", .StudentId = "1", .SubjectId = "100"}) studentsSubject.Add(New StudentsSubject With {.Id = "20", .StudentId = "1", .SubjectId = "200"}) studentsSubject.Add(New StudentsSubject With {.Id = "30", .StudentId = "2", .SubjectId = "100"}) Dim listOfStudents = From st In students Join ss In studentsSubject On ss.StudentId Equals st.Id Join sb In subjects On ss.SubjectId Equals sb.Id Select st.StudentName, st.GPA, sb.SubjectName End Sub 

7 Comments

thx for your detailed answer, I'm afraid i'm getting a "the name ss is not in the scope on the left side of equals' error.... :(
@iAteABug_And_iLiked_it: this example should work in a brand new project. Which version of VS and .NET framework are you working with? I tested with VS 2010 + .NET 4.0 in a brand new console app.
I'm on VS2012 , with .net 4.5.... maybe I'm doing something wrong...i'll spend some more time on it tomorrow and see if i can find out the issue. thx
It should be On st.Id Equals ss.StudentId, but although you went out of your way to show working code, a linq-to-objects solution is quite different than a linq-to-"sql store" one. For the latter you don't use joins but navigation properties.
@GertArnold: I don't quite understand what you are saying. The above is working code, it was tested in debugger to yield correct results. I never used LINQ-to-SQL - it's impractical for the applications we are building (insurance). So yes, my expertise is based on LINQ-to-objects. However, I can see no reason why it would not work as written, LINQ-to-SQL, meaning there should be no difference in writing the JOIN syntax, which is what OP's question is about.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.