Entity Framework - eager loading of related entiti

2019-03-26 20:12发布

问题:

Sorry the title isn't more specific - I didn't know how to describe this succinctly. I have Trips and Location that have a many-to-many relationship - straightforward except that Locations have no need to know about the Trips that use them. I've created these entities to represent this:

public class Trip
{
    public int TripId { get; set; }
    public virtual IList<TripLocation> TripLocations { get; set; }
}

public class TripLocation
{
    public int TripId { get; set; }
    public int LocationId { get; set; }

    public virtual Location Location { get; set; }
}

public class Location
{
    public int LocationId { get; set; }
    // Note: Intentionally no collection of Trips
}

I can get the Trip to eager load it's TripLocations but I can't get the TripLocations to eager load their Locations. I've tried a bunch of combinations fluent configuration and "Include"ing in the query such as

IQueryable<Trip> query = from trip in context
                              .Include(r =>r.TripLocations)
                              .Include(r => r.TripLocations.Select(tl => tl.Location))
                         select ride;

Any suggestions much appreciated!

回答1:

I recreated your scenario here and I was able to get all the results in a single query.

var a = from trip in context.Trips.Include("TripLocations.Location")
        select trip;

That's all. That's what was queried against my database:

SELECT 
[Project1].[TripId] AS [TripId], 
[Project1].[Name] AS [Name], 
[Project1].[C1] AS [C1], 
[Project1].[TripId1] AS [TripId1], 
[Project1].[LocationId] AS [LocationId], 
[Project1].[LocationId1] AS [LocationId1], 
[Project1].[Name1] AS [Name1]
FROM ( SELECT 
    [Extent1].[TripId] AS [TripId], 
    [Extent1].[Name] AS [Name], 
    [Join1].[TripId] AS [TripId1], 
    [Join1].[LocationId1] AS [LocationId], 
    [Join1].[LocationId2] AS [LocationId1], 
    [Join1].[Name] AS [Name1], 
    CASE WHEN ([Join1].[TripId] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
    FROM  [dbo].[Trips] AS [Extent1]
    LEFT OUTER JOIN  (SELECT [Extent2].[TripId] AS [TripId], [Extent2].[LocationId] AS [LocationId1], [Extent3].[LocationId] AS [LocationId2], [Extent3].[Name] AS [Name]
        FROM  [dbo].[TripLocations] AS [Extent2]
        INNER JOIN [dbo].[Locations] AS [Extent3] ON [Extent2].[LocationId] = [Extent3].[LocationId] ) AS [Join1] ON [Extent1].[TripId] = [Join1].[TripId]
)  AS [Project1]
ORDER BY [Project1].[TripId] ASC, [Project1].[C1] ASC

UPDATE:

If you wanna keep with the lambda version, this will do the work:

IQueryable<Trip> query = from ride in context.Set<Trip>()
                             .Include(t=>t.TripLocations.Select(l=>l.Location))                                     
                         select ride;

More information on the MSDN blog.



回答2:

Regarding the lambda expression, you can use context.Set like @tyron said or you can use context.Trips. For example:

IQueryable<Trip> query = from ride in context.Trips
                             .Include(t=>t.TripLocations.Select(l=>l.Location))                                     
                         select ride;

In order to make this code work, you need to define a property of type DbSet in your DbContext class like below:

public DbSet<Trip> Trips { get; set; }

Defining a property that returns DbSet is nice but at the same time it's equivalent to accesing context.Set. It's just a code style that could also be combined.



回答3:

Remove the VIRTUAL keyword on your relationship properties e.g.Location, that will disable Lazy Loading and force you to eager load.