形成查询结果(实体框架)

在执行查询时,只会返回在查询中明确请求的对象。例如,当针对 Adventure Works 销售模型的查询返回 Customer 对象时,默认情况下不返回相关的 SalesOrderHeader 对象,即使 CustomerSalesOrderHeader 之间存在关系也是如此。此行为可确保应用程序始终知道从对象查询返回的数据的范围。默认情况下,会始终返回表示实体类型之间的关联的关系对象。如果对象是基于 EDM 的概念性架构生成的,则会在关联两端为实体对象生成导航属性。这些导航属性返回一对一或多对一关系的“一”端上的 EntityReference,或是一对多或多对多关系的“多”端上的 EntityCollection。有关更多信息,请参见 实体数据模型关系

通过使用导航属性,您可以编写 Entity SQL 或 LINQ to Entities 查询对这些关系进行显式导航。有关更多信息,请参见如何:使用导航属性导航关系(实体框架)。不过,您不需要为了形成查询结果而在查询中对关系进行显式导航。若要扩展查询结果,以便加载引用的对象,可以使用两种其他方式:您可以指定查询路径,也可以使用导航属性显式加载相关对象。若要对结果进行更多控制,可以定义查询路径,然后仅显式加载所选相关对象。

在考虑要使用哪个选项时,请注意在对数据库的请求数与单个查询中返回的数据量之间进行权衡。查询路径定义查询所返回的对象图。在定义查询路径时,仅需对数据库请求一次,即可在单个结果集中返回查询路径所定义的所有对象。显式加载对象需要多次往返数据库,并且可能需要多个活动结果集,但是返回的数据量仅限于被加载对象。

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

若要指定查询路径,请将对象图的字符串表示形式传递给 ObjectQueryInclude 方法。此路径指定在执行对象查询时要返回哪些相关对象。例如,针对 Contact 对象的查询定义的查询路径可确保返回每个相关的 SalesOrderHeaderSalesOrderDetail。在下面使用 LINQ to Entities、Entity SQL 和查询生成器方法的查询中,对此进行了演示。

  • LINQ to Entities

    ' Define a LINQ query with a path that returns 
    ' orders and items for a contact.
    Dim contacts = (From contact In context.Contact _
        .Include("SalesOrderHeader.SalesOrderDetail") _
        Select contact).FirstOrDefault()
    
    // Define a LINQ query with a path that returns 
    // orders and items for a contact.
    var contacts = (from contact in context.Contact
                  .Include("SalesOrderHeader.SalesOrderDetail")
                  select contact).FirstOrDefault();
    
  • Entity SQL

    ' Define an object query with a path that returns 
    ' orders and items for a specific contact.              
    Dim queryString As String = _
        "SELECT VALUE TOP(1) Contact FROM " + _
        "AdventureWorksEntities.Contact AS Contact"
    
    ' Define the object query with the query string.
    Dim contactQuery As New ObjectQuery(Of Contact)(queryString, _
        context, MergeOption.NoTracking)
    
    Dim contact As Contact = _
    contactQuery.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.              
    string queryString =
        @"SELECT VALUE TOP(1) Contact FROM " + 
        "AdventureWorksEntities.Contact AS Contact";
    
    // Define the object query with the query string.
    ObjectQuery<Contact> contactQuery = new ObjectQuery<Contact>(queryString, 
        context, MergeOption.NoTracking);
    
    Contact contact =
        contactQuery.Include("SalesOrderHeader.SalesOrderDetail")
        .FirstOrDefault();
    
  • 查询生成器方法

    ' Create an object query with a path that returns orders and items for a contact.
    Dim contact As Contact = _
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail") _
        .FirstOrDefault()
    
    // Define an object query with a path that returns 
    // orders and items for a specific contact.
    Contact contact =
        context.Contact.Include("SalesOrderHeader.SalesOrderDetail")
        .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.SalesOrderHeader.Include("SalesOrderDetail").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.SalesOrderHeader.Include("SalesOrderDetail").Include("Address");
    
  • 如果使用查询路径,看似简单的对象查询也可能需要对数据源执行复杂的命令。这是因为,在单个查询中返回相关对象需要一个或多个联接。对复杂 EDM(如具有继承关系的实体或包含多对多关系的路径)进行的查询的复杂性将进一步加大。使用 ToTraceString 方法可以查看将由 ObjectQuery 生成的命令。有关更多信息,请参见对象查询(实体框架)。如果查询路径包含过多相关对象,或对象包含过多行数据,数据源可能无法完成查询。如果查询所需的中间临时存储区超过数据源的容量,则会出现这种情况。出现这种情况时,通过显式加载相关对象可以降低数据源查询的复杂性。

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

显式加载相关对象

若要显式加载相关对象,必须调用导航属性所返回的相关端的 Load 方法。对于一对多关系,请调用 EntityCollectionLoad 方法,而对于一对一关系,请调用 EntityReferenceLoad。这样可将相关对象数据加载到对象上下文中。当查询返回一个对象集合时,您可以循环访问该集合并调用 Load 方法,来加载该集合中每个对象的相关对象(如属于 SalesOrderHeader 对象的每个 SalesOrderDetail 对象)。在下面的示例中,为指定的 SalesOrderHeader 对象显式加载了 SalesOrderDetail 对象:

' Load the items for the order if not already loaded.
If Not order.SalesOrderDetail.IsLoaded Then
    order.SalesOrderDetail.Load()
End If
// Load the items for the order if not already loaded.
if (!order.SalesOrderDetail.IsLoaded)
{
    order.SalesOrderDetail.Load();
}
Note注意

当您在 foreach (C#) 或 For Each (Visual Basic) 枚举过程中调用 Load 方法时,对象服务会尝试打开一个新的数据读取器。除非您已经通过在连接字符串中指定 multipleactiveresultsets=true 启用了多个活动结果集,否则此操作将失败。有关更多信息,请参见 MSDN 上的 Using Multiple Active Result Sets (MARS)(使用多个活动结果集 (MARS))。您还可以将查询结果加载到 List 集合中,这样,会关闭数据读取器,您也可以对集合进行枚举以加载引用的对象。

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

查询相关对象

因为 EntityCollection 类实现 IEnumerable 接口,所以可以使用 LINQ 查询加载到 EntityCollection(由导航属性返回)中的对象集合。无论对象是通过指定查询路径隐式加载到对象上下文中还是通过调用 Load 方法显式加载的,都可以使用这种方法。

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

ObjectQuery 返回实体对象形式的 EDM 数据。但是,如果查询投影包含某个导航属性,则 ObjectQuery 返回包含相关对象的嵌套的 DbDataRecord。有关更多信息,请参见如何:使用导航属性导航关系(实体框架)

另请参见

概念

以对象形式查询数据(实体框架)
对象查询(实体框架)
查询生成器方法(实体框架)