Share via


加载相关对象(实体框架)

本主题介绍可用于加载相关实体的模式。 实体类型可以定义在数据模型中表示关联的导航属性。 可以使用这些属性加载与所定义的关联返回的实体相关的实体。 如果实体是基于数据模型生成的,则会在关联两端为实体生成导航属性。 这些导航属性在一对一或多对一关系的“一”端返回一个引用,或在一对多或多对多关系的“多”端返回一个集合。 有关更多信息,请参见导航属性定义和管理关系(实体框架)

以下模式描述您可以加载相关实体的方法:

加载模式 说明

在查询中指定

通过使用导航属性,您可以编写 Entity SQL 或 LINQ to Entities 查询对关系进行显式导航。 当您执行此类查询时,将返回在最外部的查询投影中作为导航属性包含的相关实体。 有关更多信息,请参见

如何:使用导航属性导航关系(实体框架).

显式加载

将实体显式加载到 ObjectContext 需要多次往返数据库,并且可能需要多个活动结果集,但是返回的数据量仅限于所加载的实体。 可以对 EntityCollectionEntityReference 使用 Load 方法或对 ObjectContext 使用 LoadProperty 方法,以便从数据源显式检索相关实体。 对于 Load 方法的每个调用都会打开与数据库的连接,以检索相关信息。 这可确保在没有对相关实体的显式请求时,始终不会执行查询。 显式加载是实体框架 的默认行为。

Bb896272.note(zh-cn,VS.100).gif注意:
在调用 Load 之前,有关相关实体的少量信息已加载到 ObjectContext 中。

有关更多信息,请参见本主题的Explicitly Loading Related Objects一节。

延迟加载

对于这种类型的加载,在您访问导航属性时,会从数据源自动加载相关实体。 使用此加载类型时,请注意,如果实体尚未在 ObjectContext 中,则您访问的每个导航属性都会导致针对数据源执行一个单独的查询。

有关更多信息,请参见本主题的Lazy Loading一节。

预先加载

使用 Include 定义查询路径

当您了解应用程序需要的相关实体的图形的确切形状时,可以使用 ObjectQueryInclude 方法来定义查询路径,此查询路径控制将哪些相关实体作为初始查询的一部分返回。 当定义查询路径时,仅需对数据库请求一次,即可在单个结果集中返回查询路径所定义的所有实体,并且属于在路径中定义的类型的所有相关实体将随查询返回的每个对象一起加载。

有关更多信息,请参见本主题的Defining a Query Path to Shape Query Results一节。

显式加载相关实体对象

若要显式加载相关实体,必须对导航属性所返回的相关端调用 Load 方法。 对于一对多关系,请对 EntityCollection 调用 Load 方法,而对于一对一关系,请对 EntityReference 调用 Load 方法。 如果使用的是 POCO 实体,请对 ObjectContext 使用 LoadProperty 方法。 有关更多信息,请参见加载相关 POCO 实体(实体框架)LoadProperty 方法也可以用于从 EntityObject 派生的实体。 这些方法将相关对象数据加载到对象上下文中。 当查询返回结果时,可以使用 foreach 循环(在 Visual Basic 中为 For Each...Next)枚举整个对象集合,并对结果中每个实体的 EntityReferenceEntityCollection 属性按条件调用 Load 方法。

Bb896272.note(zh-cn,VS.100).gif注意:
当在 foreach (C#) 或 For Each (Visual Basic) 枚举过程中调用 Load 方法时,实体框架 会尝试打开一个新的数据读取器。除非您已经通过在连接字符串中指定 multipleactiveresultsets=true 来启用多个活动结果集,否则此操作将失败。有关更多信息,请参见 MSDN 上的 Using Multiple Active Result Sets (MARS)(使用多个活动结果集 (MARS))。您还可以将查询结果加载到 List 集合中,这会关闭数据读取器并使您能够对集合进行枚举以加载引用的实体。

有关更多信息,请参见如何:显式加载相关对象(实体框架)

定义查询路径以形成查询结果

若要指定查询路径,请将对象图的字符串表示形式传递给 ObjectQueryInclude 方法。 此路径指定在执行对象查询时要返回哪些相关实体。 例如,针对 Contact 对象的查询定义的查询路径可确保返回每个相关的 SalesOrderHeaderSalesOrderDetail。 如以下查询所示:

' Define a LINQ query with a path that returns 
' orders and items for a contact. 
Dim contacts = (From contact In context.Contacts.Include("SalesOrderHeaders.SalesOrderDetails") _
    Select contact).FirstOrDefault()
// Define a LINQ query with a path that returns 
// orders and items for a contact.
var contacts = (from contact in context.Contacts
              .Include("SalesOrderHeaders.SalesOrderDetails")
                select contact).FirstOrDefault();

在定义查询路径时,应考虑以下注意事项:

  • 查询路径可以用于查询生成器方法和 LINQ 查询。

  • 在调用 Include 时,查询路径仅在 ObjectQuery 的返回实例上有效。 不影响 ObjectQuery 的其他实例和对象上下文本身。

  • 因为 Include 返回查询对象,所以可以对一个 ObjectQuery 多次调用此方法,从而包括来自多个关系的实体,如下面的示例所示:

    ' Create a SalesOrderHeader query with two query paths, 
    ' one that returns order items and a second that returns the 
    ' billing and shipping addresses for each order. 
    Dim query As ObjectQuery(Of SalesOrderHeader) = context.SalesOrderHeaders.Include("SalesOrderDetails").Include("Address")
    
    // Create a SalesOrderHeader query with two query paths, 
    // one that returns order items and a second that returns the 
    // billing and shipping addresses for each order.
    ObjectQuery<SalesOrderHeader> query =
        context.SalesOrderHeaders.Include("SalesOrderDetails").Include("Address");
    
  • 如果使用查询路径,看似简单的对象查询也可能需要对数据源执行复杂的命令。 之所以发生这种情况,原因是在单个查询中返回相关对象要求具有一个或多个联接,这可能导致从数据源返回的每个相关实体出现冗余数据。 对复杂模型(如具有继承关系的实体或包含多对多关系的路径)进行的查询的复杂性将进一步加大。 使用 ToTraceString 方法可以查看将由 ObjectQuery 生成的命令。 如果查询路径包含过多相关对象,或对象包含过多行数据,数据源可能无法完成查询。 如果查询所需的中间临时存储区超过数据源的容量,则会出现这种情况。 出现这种情况时,通过显式加载相关对象或启用延迟加载可以降低数据源查询的复杂性。 如果在优化复杂查询后仍然频繁超时,请考虑通过设置 CommandTimeout 属性来增大超时值。

有关更多信息,请参见如何:使用查询路径调整结果(实体框架)

延迟加载实体对象

实体框架 支持相关实体的延迟加载。 在实体框架 运行时中,ObjectContext 实例中 LazyLoadingEnabled 属性的默认值为 false。 但是,如果您使用实体框架 工具创建新模型和对应的生成类,则在对象上下文的构造函数中,LazyLoadingEnabled 设置为 true。 启用延迟加载后,在某个导航属性的 get 访问器以编程方式访问相关实体之前,将不会从数据源加载它们。 若要禁用延迟加载,请对于由 System.Data.Objects.ObjectContext.ContextOptions 属性返回的 ObjectContextOptions 实例将 LazyLoadingEnabled 属性设置为 false

延迟加载可以与预先加载一起使用。 通过这种方式,可以使用查询路径定义基本数据图,并可以根据需要加载原始查询路径中未包括的其他相关实体。 有关更多信息,请参见如何:使用延迟加载来加载相关对象(实体框架)

使用延迟加载时,应注意以下事项:

  • 对于同时返回单个实体(如 EntityReference)和实体集合(如 EntityCollection)的导航属性,支持延迟加载。

  • 如果启用了延迟加载并且已加载了相关实体,则不会再次加载它。

  • 对于处于 Detached 状态的实体,支持延迟加载。 在此情况下,相关对象还将以 Detached 状态返回。

  • 延迟加载行为是由用于从数据源检索对象(即使实体是使用 NoTracking MergeOption 加载的)或对象所添加或附加到的 ObjectContext 实例确定的。 因此,一旦释放了此上下文,将无法更改延迟加载行为,并且任何进一步的延迟加载操作都将失败。

  • 当对实体进行序列化时,请考虑禁用延迟加载。 否则,将触发延迟加载,并且序列化实体包含的数据可能超过预期。

当您将延迟加载用于 POCO 实体时,应考虑其他注意事项。 有关更多信息,请参见加载相关 POCO 实体(实体框架)

查询相关实体对象

通过对 EntityCollection 调用 CreateSourceQuery 方法,您可以查询相关对象,而不需要首先将对象加载到集合中。 CreateSourceQuery 返回一个 ObjectQuery,当执行它时,将与调用 Load 方法返回同一组对象。 将查询生成器方法应用于此对象查询,可以进一步筛选加载到集合中的对象。 有关更多信息,请参见如何:查询 EntityCollection 中的相关对象(实体框架)

ObjectQuery 以实体形式返回实体数据。 但是,如果最外部的查询投影包含某个导航属性,则此查询还返回此导航访问的相关实体。 有关更多信息,请参见如何:使用导航属性导航关系(实体框架)

性能注意事项

当您选择用于加载相关实体的模式时,请权衡和数据源建立连接的数量和时间与使用单个查询返回的数据量和复杂性等来考虑每种方法的行为。 预先加载将所有相关实体和所查询的实体在单个查询中一起返回。 这意味着,尽管与数据源之间只建立了一个连接,但在初始查询中将返回更多的数据。 此外,查询路径导致查询更为复杂,因为针对数据源执行的查询要求更多联接。

借助于显式和延迟加载,您可以推迟对于相关对象数据的请求,直到实际需要数据。 这会生成复杂度较小的初始查询,返回的总数据也较少。 但是,相关对象的每次后续加载都会与数据源建立连接并执行一个查询。 在延迟加载的情况下,只要访问导航属性并且相关实体尚未加载,就会建立此连接。 如果您想了解初始查询返回了哪些相关实体或如何管理从数据源加载相关实体的时间,则应考虑禁用延迟加载。 在实体框架 生成的对象上下文的构造函数中,延迟加载处于启用状态。

有关更多信息,请参见性能注意事项(实体框架)

另请参见

概念

查询概念模型(实体框架)
对象查询(实体框架)
查询生成器方法(实体框架)