This is a common scenario for customers who do multiple Include() calls within their Linq queries and where more than one of those Include() calls are to one-to-many collections. A customer explained the issue in great detail at http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/5b9db2f3-f156-4ff7-b764-f67669a2a26d. This is a rather complex scenario, but apparently not so uncommon.
For example, suppose Projects has a 1..* relationship to ForeignLoggers AND ALSO a 1..* relationship to Loggers. Furthermore, suppose that Loggers has a 1..* relationship to Inverters AND ALSO a 1..* relationship to Sensors. Now have the following query:
var query = from p in ctx.Projects.Include("Loggers")
.Include("ForeignLoggers")
.Include("Loggers.Sensors")
.Include("Loggers.Inverters")
.Include("Loggers.Inverters.InverterType")
This query will produce a query that includes a CROSS APPLY over the UNION ALL of the Sensors and the Inverters that are related to the Loggers.
This query is very complex by nature but the query compiler could be enhanced to detect cases like this where the CROSS APPLY can be transformed to a CROSS JOIN. If in this example query we leave out the inclusion of ForeignLoggers then the code generated includes an OUTER APPLY instead of a CROSS APPLY.
The transformation should occur inside System.Data.Entity.Core.Query.PlanCompiler.ApplyOpRules and optionally in System.Data.Entity.Core.Query.PlanCompiler.FilterOpRules if it is part of the cases where OuterApply is transformed into CrossApply.
Note that the OuterApplyOp is being created by the subquery tracking visitor, thus appearing like there could be room for improvement from within the subquery tracking visitor instead of having to transform the OuterApply to a LeftOuterJoin, but this would require additional investigation.
Find a repro scenario in the attached zip file.
For example, suppose Projects has a 1..* relationship to ForeignLoggers AND ALSO a 1..* relationship to Loggers. Furthermore, suppose that Loggers has a 1..* relationship to Inverters AND ALSO a 1..* relationship to Sensors. Now have the following query:
var query = from p in ctx.Projects.Include("Loggers")
.Include("ForeignLoggers")
.Include("Loggers.Sensors")
.Include("Loggers.Inverters")
.Include("Loggers.Inverters.InverterType")
This query will produce a query that includes a CROSS APPLY over the UNION ALL of the Sensors and the Inverters that are related to the Loggers.
This query is very complex by nature but the query compiler could be enhanced to detect cases like this where the CROSS APPLY can be transformed to a CROSS JOIN. If in this example query we leave out the inclusion of ForeignLoggers then the code generated includes an OUTER APPLY instead of a CROSS APPLY.
The transformation should occur inside System.Data.Entity.Core.Query.PlanCompiler.ApplyOpRules and optionally in System.Data.Entity.Core.Query.PlanCompiler.FilterOpRules if it is part of the cases where OuterApply is transformed into CrossApply.
Note that the OuterApplyOp is being created by the subquery tracking visitor, thus appearing like there could be room for improvement from within the subquery tracking visitor instead of having to transform the OuterApply to a LeftOuterJoin, but this would require additional investigation.
Find a repro scenario in the attached zip file.