Open nettashamir-allocate opened 11 months ago
/cc @roji @maumar
many-to-many loader produces the following query:
DbSet<Airport>()
.Where(e => EF.Property<int>(e, "ID") == __p_0)
.SelectMany(e => e.Airlines)
.NotQuiteInclude(e => EF.Property<IEnumerable<Airport>>(e, "Airports")
.Where(e => EF.Property<int>(e, "ID") == __p_0))
.AsTracking()
which then gets converted in nav expansion to:
DbSet<Airport>()
.Where(a => EF.Property<int>(a, "ID") == __p_0)
.SelectMany(
collectionSelector: a => DbSet<Dictionary<string, object>>("Airport_Airline")
.Where(a0 => EF.Property<int?>(a, "ID") != null && object.Equals(
objA: (object)EF.Property<int?>(a, "ID"),
objB: (object)EF.Property<int?>(a0, "AirportID")))
.Join(
inner: DbSet<Airline>(),
outerKeySelector: a0 => (object)EF.Property<int?>(a0, "AirlineID"),
innerKeySelector: a1 => (object)EF.Property<int?>(a1, "ID"),
resultSelector: (a0, a1) => new TransparentIdentifier<Dictionary<string, object>, Airline>(
Outer = a0,
Inner = a1
))
.Select(ti => ti.Inner),
resultSelector: (a, c) => new TransparentIdentifier<Airport, Airline>(
Outer = a,
Inner = c
))
.Select(ti0 => IncludeExpression(
EntityExpression:
ti0.Inner,
NavigationExpression:
MaterializeCollectionNavigation(
Navigation: Airline.Airports,
subquery: DbSet<Dictionary<string, object>>("Airport_Airline")
.Where(a2 => EF.Property<int?>(ti0.Inner, "ID") != null && object.Equals(
objA: (object)EF.Property<int?>(ti0.Inner, "ID"),
objB: (object)EF.Property<int?>(a2, "AirlineID")))
.Join(
inner: DbSet<Airport>(),
outerKeySelector: a2 => (object)EF.Property<int?>(a2, "AirportID"),
innerKeySelector: a3 => (object)EF.Property<int?>(a3, "ID"),
resultSelector: (a2, a3) => new TransparentIdentifier<Dictionary<string, object>, Airport>(
Outer = a2,
Inner = a3
))
.Where(ti1 => EF.Property<int>(ti1.Inner, "ID") == __p_0)
.Select(ti1 => IncludeExpandingExpressionVisitor.FetchJoinEntity<Dictionary<string, object>, Airport>(
joinEntity: ti1.Outer,
targetEntity: ti1.Inner))), Airports)
)
when we Load Airlines from Airport, we not only query Airport -> AirportAirline -> Airline, but also include back references from Airline to Airport. That's where the extra complexity and work is coming from
Is there any news on this? I have a similar problem, and is aggravated by having an owned collection in the main entity, making the query to the db loads up tons of unused data.
when we Load Airlines from Airport, we not only query Airport -> AirportAirline -> Airline, but also include back references from Airline to Airport. That's where the extra complexity and work is coming from
@maumar Is there any way to stop it from including the back references if I don't want to fetch them eagerly?
@nettashamir-allocate you can use regular Include query, like so:
var airport = context.Airports.First();
_ = context.Airports.Where(a => a.ID == airport.ID).Include(x => x.Airlines).ToList();
which produces sql like this:
SELECT [a].[ID], [a].[Name], [s].[AirlineID], [s].[AirportID], [s].[ID], [s].[Name]
FROM [Airport] AS [a]
LEFT JOIN (
SELECT [a0].[AirlineID], [a0].[AirportID], [a1].[ID], [a1].[Name]
FROM [Airport_Airline] AS [a0]
INNER JOIN [Airline] AS [a1] ON [a0].[AirlineID] = [a1].[ID]
) AS [s] ON [a].[ID] = [s].[AirportID]
WHERE [a].[ID] = @__airport_ID_0
ORDER BY [a].[ID], [s].[AirlineID], [s].[AirportID]
N'@__airport_ID_0 int',@__airport_ID_0=1
Note that this will only populate Airlines navigation of the selected Airport with all the airlines, however airlines themselves will only point to this one airport, even if there should be other airports associated with it.
Description
In our project we use explicit lazy loading of all associations. When loading entities via a many-to-many association (something like Airports - Airlines where an Airport can host many Airlines and an Airline can fly from many Airports) I would expect SQL of the form:
but instead I get:
This is overly complex and takes twice as long as the simple version.
Full Repro Code
Tests
Generated SQL
Mirror image sql is generated when queried from the other end.
This results in a query plan which looks like:
Whereas this SQL:
results in this query plan:
Repro Project
RedundantJoins-CrossLinks.zip
Include provider and version information
EF Core version: 7.0.5 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET 6.0 Operating system: WIndows 11 IDE: Visual Studio 2022 17.7