【问题标题】:Entity Framework - eager loading of related entities实体框架 - 相关实体的急切加载
【发布时间】:2012-02-07 01:34:56
【问题描述】:

抱歉,标题没有更具体 - 我不知道如何简洁地描述这一点。 我有具有多对多关系的 Trips 和 Location - 很简单,只是 Locations 不需要知道使用它们的 Trips。我创建了这些实体来表示这一点:

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
}

我可以让 Trip 急切加载它的 TripLocations,但我无法让 TripLocations 急切加载它们的位置。我在查询中尝试了一堆组合流畅的配置和“包含”,例如

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

非常感谢任何建议!

【问题讨论】:

  • 对不起应该是“旅行”
  • 为什么你的模型中有TripLocation 实体?此实体中是否还有其他您未显示的属性?否则,您可以从模型中完全删除此实体,并直接在 Trip 和 Location 之间创建多对多关系。在您当前的模型中,您实际上有两个一对多而不是多对多的关系。
  • TripLocation 在那里,因为我不想要位置中的旅行集合,而且我不知道如何使用流利的配置来进行多对多的操作。由于@tyron 的回答帮助我按原样工作,我要回去看看我是否能按照你的建议做。
  • 只需使用modelBuilder.Entity&lt;Trip&gt;().HasMany(t =&gt; t.Locations).WithMany().Map(...);。使用不带参数的WithMany() 适用于Location 中没有Trips 集合的情况。
  • @tyron:我不确定我是否理解正确,但我的意思是:如果您在连接表中没有其他属性,您可以创建多对多没有中间实体的关系 (TripLocation)。如果您具有这样的附加属性,则根本无法创建多对多关系,则必须创建两个一对多关系并将连接表作为模型中的实体公开: stackoverflow.com/a/7053393/270591

标签: entity-framework ef-code-first eager-loading


【解决方案1】:

我在这里重新创建了您的场景,并且能够在一个查询中获得所有结果。

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

就是这样。这就是针对我的数据库查询的内容:

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

更新:

如果您想继续使用 lambda 版本,这将完成工作:

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

有关MSDN blog的更多信息。

【讨论】:

  • 谢谢!您的回答证实我走在正确的轨道上,并让我从不同的角度看待实际代码(上面的示例正是我认为实际代码的相关部分)。
  • 感谢 lambda 等效项。
【解决方案2】:

关于 lambda 表达式,您可以像 @tyron 所说的那样使用 context.Set,也可以使用 context.Trips。例如:

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

为了使这段代码工作,你需要在你的 DbContext 类中定义一个 DbSet 类型的属性,如下所示:

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

定义一个返回 DbSet 的属性很好,但同时它相当于访问 context.Set。它只是一种代码风格,也可以组合。

【讨论】:

    【解决方案3】:

    删除关系属性(例如位置)上的 VIRTUAL 关键字,这将禁用延迟加载并强制您急切加载。

    【讨论】:

      猜你喜欢
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-23
      • 2012-09-10
      • 1970-01-01
      • 2011-05-03
      相关资源
      最近更新 更多