My lack of understanding LINQ

2019-08-05 06:43发布

问题:

Ok I have several tables linked together.

Departments (k = departmentID)
Functions (k = functionID, fk = departmentID)
Processes (k = processID, fk = functionID)
Procedures (k = procedureID, fk = processID)

So they all have their relationships setup when trying to come up with some linq I run into some oddities.

The below code will return me

Departments.Select(s => s.Functions)

But when trying to expand that query further it will not let me. I'd like to be able to join all of the above tables and pull information out of them as I need it.

Departments.Select(s => s.Functions.Process.Procedure) // Errors out

Further more I can do the following:

Functions.Select(s => s.Processes)

It seems it will do it for two tables but no more than 2? Am I missing something?

回答1:

Here's the thing. The Departments to Functions relationship is one to many. So when you just write,

 Departments

you have a collection of Department objects. Since each Department object has its own collection of Function objects, doing:

 Departments.Select(departmentObject => departmentObject.Functions)

gives you a collection of collections of Function objects.

If you want to aggregate all these together, you have to use a different method, specifically

 Departments.SelectMany(departmentObject => departmentObject.Functions)

which says "get the collection of collections of Function objects and make them into one big collection of Function objects"

What you are trying to do is:

Departments.Select(departmentObject => departmentObject .Functions.Process.Procedure)

But, this can't possibly work, because you are asking a collection of Function objects for its Process property. But, a collection of Function objects doesn't have a Process property. A Function object, itself, has a Process property. So, what you are really trying to do is:

 Departments
     .SelectMany(departmentObject => departmentObject.Functions)
     .Select(functionObject => functionObject.Process.Procedure)

which basically translates to "get the collection of collections of Function objects and make them into one big collection of Function objects. Then, get the Procedure property of the Process property of each Function object".

So, what you should be expecting here is just a collection of the procedures that are performed by the processes of any function associated with any of the departments.

Note, that if there is any overlap in the Functions of Departments, in the Processes of Functions, or in the Procedures of Processes, then you may get some duplicate Procedures in your end result. If this isn't what you want, then you can use the Distinct() method to remove duplicates, and you should do so at any point where there is overlap, but there should not be duplication. You can get the same result by simply adding Distinct() to the end of the query, but you will have better performance if you kill the duplicates along the way. So, in reality, your query will probably look like something in between the following and previous query, depending on how much overlap there is and how much duplication you want:

 Departments
     .SelectMany(departmentObject => departmentObject.Functions)
     .Distinct()
     .Select(functionObject => functionObject.Process)
     .Distinct()
     .Select(processObject => processObject.Procedure)
     .Distinct();


标签: c# linq lambda