`

Entity Framework 学习总结之十:加载相关对象

 
阅读更多

加载相关对象

实体类型可以定义在数据模型中表示关联的导航属性。可以使用这些属性加载与所定义的关联返回的实体相关的实体。如果实体是基于数据模型生成的,则会在关联两端为实体生成导航属性。这些导航属性在一对一或多对一关系的 端返回一个引用,或在一对多或多对多关系的 端返回一个集合。

 

加载相关实体的方法:

·    在查询中指定

·    显式加载

·    延迟加载

·    预先加载或使用 Include 定义查询路径

 

在查询中指定

导航属性:实体框架中的导航属性提供了一种在两个实体类型之间导航关联的方法。导航属性在概念模型中由 NavigationProperty 元素 (CSDL) 定义。针对对象参与到其中的每个关系,各对象均可以具有导航属性。使用导航属性,可以在两个方向上导航和管理关系,如果重数为一或者零或一,则返回 EntityReference<(Of <(<'TEntity>)>)> ,或者如果重数为多个,则返回 EntityCollection<(Of <(<'TEntity>)>)> 。也可以选择单向导航,这种情况下可以删除导航属性。

编辑和删除导航属性(实体数据模型工具): http://msdn.microsoft.com/zh-cn/library/bb738475.aspx

 

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

示例类图:每个客户拥有一种客户类型和多个邮箱地址。

 

LINQ to Entities 示例

代码片断:

using (var context = new EFRecipesEntities ())

{

    // 此处如果不在 select 中显示调用相关对象,则在遍历时会出错。

    var customers = from customer in context.Customers

                    select new { Name = customer.Name, CustomerType = customer.CustomerType, CustomerEmails = customer.CustomerEmails };

 

    Console .WriteLine("Customers" );

    Console .WriteLine("=========" );

    foreach (var customer in customers)

    {

        Console .WriteLine("{0} is a {1}, email address(es)" , customer.Name, customer.CustomerType.Description);

        foreach (var email in customer.CustomerEmails)

        {

            Console .WriteLine("\t{0}" , email.Email);

        }

    }

}

 

Entity SQL 示例

代码片断:

using (var context = new EFRecipesEntities ())

{

    string esql = @"SELECT c.Name,c.CustomerType,c.CustomerEmails FROM Customers AS c" ;

 

    foreach (DbDataRecord rec in new ObjectQuery <DbDataRecord >(esql, context))

    {

        CustomerType customerTypes = rec[1] as CustomerType ;

        List <CustomerEmail > customerEmails = rec[2] as List <CustomerEmail >;

 

        Console .WriteLine("{0} is a {1}, email address(es)" , rec[0], customerTypes.Description);

 

        foreach (CustomerEmail customerEmail in customerEmails)

        {

            Console .WriteLine("\t{0}" , customerEmail.Email);

        }

    }

}

 

运行结果:

 

显示加载

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

 

说明说明:在调用 Load 之前,有关相关实体的少量信息已加载到 ObjectContext 中。

 

若要显式加载相关实体,必须对导航属性所返回的相关端调用 Load 方法。对于一对多关系,请对 EntityCollection<(Of <(<'TEntity>)>)> 调用 Load  方法,而对于一对一关系,请对 EntityReference<(Of <(<'TEntity>)>)> 调用 Load  方法。如果使用的是 POCO 实体,请对 ObjectContext  使用 LoadProperty  方法。 LoadProperty 方法也可以用于从 EntityObject  派生的实体。这些方法将相关对象数据加载到对象上下文中。当查询返回结果时,可以使用 foreach  循环枚举整个对象集合,并对结果中每个实体的 EntityReference<(Of <(<'TEntity>)>)> EntityCollection<(Of <(<'TEntity>)>)> 属性按条件调用 Load 方法。

 

说明:

当在 foreach (C#) 枚举过程中调用 Load 方法时,实体框架会尝试打开一个新的数据读取器。除非您已经通过在连接字符串中指定 multipleactiveresultsets=true 来启用多个活动结果集,否则此操作将失败。 还可以将查询结果加载到 List<(Of <(<'T>)>)> 集合中,这会关闭数据读取器并使您能够对集合进行枚举以加载引用的实体。

 

示例: 使用 EntityCollection<(Of <(<'TEntity>)>)> Load  方法来显式加载单个客户的所有邮箱项。

代码片断:

using (var context = new EFRecipesEntities ())

{

    Customer customer = context.Customers.Where("it.CustomerID=@CustomerID" , new ObjectParameter ("CustomerID" , 13)).First();

    if (!customer.CustomerEmails.IsLoaded)

    {

        // 显示加载

        customer.CustomerEmails.Load();

    }

 

    Console .WriteLine("Name {0}" , customer.Name);

    foreach (CustomerEmail customerEmail in customer.CustomerEmails)

    {

        Console .WriteLine("Email {0}" , customerEmail.Email);

    }

}

 

运行结果:

 

示例: 使用 EntityCollection<(Of <(<'TEntity>)>)> CreateSourceQuery  方法,以仅加载 1 个具有相关 CustomerEmail 对象(这些对象属于单个 Contact )的 CustomerEmail 对象。   随后,此查询结果会附加到原始 CustomerEmail EntityCollection<(Of <(<'TEntity>)>)>

代码片断:

Customer customer = context.Customers.Where("it.CustomerID=@CustomerID" , new ObjectParameter ("CustomerID" , 13)).First();

customer.CustomerEmails.Attach(customer.CustomerEmails.CreateSourceQuery().Take(1));

 

//if (!customer.CustomerEmails.IsLoaded)

//{

//    customer.CustomerEmails.Load();

//}

...

 

延迟加载

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

 

延迟加载可以与预先加载一起使用。 通过这种方式,可以使用查询路径定义基本数据图,并可以根据需要加载原始查询路径中未包括的其他相关实体。

 

延迟加载时注意:

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

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

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

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

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

 

代码片断:(预先加载有介绍)

context.ContextOptions.LazyLoadingEnabled = true;

 

预先加载

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

 

例如: “context.CustomerTypes.Include("Customers.CustomerEmails")”  返回 CustomerTypes 相关的 Customers CustomerEmails 对象的查询路径。可以对一个 ObjectQuery<(Of <(<'T>)>)> 多次调用此方法,从而包括来自多个关系的实体,如 “context.Customers.Include("CustomerType").Include("CustomerEmails")”

 

代码片断:

using (var context = new EFRecipesEntities ())

{

    var web = new CustomerType { Description = "Web Customer" , CustomerTypeId = 1 };

    var retail = new CustomerType { Description = "Retail Customer" , CustomerTypeId = 2 };

 

    // 添加客户 Joan Smith ,所属类型 " web " ,拥有两个 Email

    var customer = new Customer { Name = "Joan Smith" , CustomerType = web };

    customer.CustomerEmails.Add(new CustomerEmail { Email = "jsmith@gmail.com" });

    customer.CustomerEmails.Add(new CustomerEmail { Email = "joan@smith.com" });

    context.Customers.AddObject(customer);

 

    customer = new Customer { Name = "Bill Meyers" , CustomerType = retail };

    customer.CustomerEmails.Add(new CustomerEmail { Email = "bmeyers@gmail.com" });

    context.Customers.AddObject(customer);

    // 提交添加

    context.SaveChanges();

}

 

using (var context = new EFRecipesEntities ())

{

    // EF4 默认不开启延迟加载功能,如果略掉 Include ,则在访问子项时会出错。

    var customers = context.Customers.Include("CustomerType" ).Include("CustomerEmails" );

    Console .WriteLine("Customers" );

    Console .WriteLine("=========" );

    foreach (var customer in customers)

    {

        Console .WriteLine("{0} is a {1}, email address(es)" , customer.Name, customer.CustomerType.Description);

        foreach (var email in customer.CustomerEmails)

        {

            Console .WriteLine("\t{0}" , email.Email);

        }

    }

}

 

using (var context = new EFRecipesEntities ())

{

    // 必须使用 Include("Customers.CustomerEmails" ) 来加载。

    //var customTypes = context.CustomerTypes.Include("Customers").Include("CustomerEmails");

    var customTypes = context.CustomerTypes.Include("Customers.CustomerEmails" );

    Console .WriteLine("\nCustomers by Type" );

    Console .WriteLine("=================" );

    foreach (var customerType in customTypes)

    {

        Console .WriteLine("Customer type: {0}" , customerType.Description);

        foreach (var customer in customerType.Customers)

        {

            Console .WriteLine("{0}" , customer.Name);

            foreach (var email in customer.CustomerEmails)

            {

                Console .WriteLine("\t{0}" , email.Email);

            }

       }

    }

}

Console .WriteLine("Press <enter> to continue..." );

Console .ReadLine();

 

延迟加载: LazyLoadingEnabled() 属性用来设置相关对象的惰性加载行为。 LazyLoadingEnabled() 的默认值为 false 。使用这种加载时,请注意如果对象已经不在 ObjectContext  中,您访问的每个导航属性将导致对数据源执行单独的查询。开启延迟加载功能, context.ContextOptions.LazyLoadingEnabled = true ; 则不用显示使用 Include 方法来加载相关实体。

 

定义查询路径时注意事项:

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

·    在调用 Include 时,查询路径仅在 ObjectQuery<(Of <(<'T>)>)> 的返回实例上有效。 不影响 ObjectQuery<(Of <(<'T>)>)> 的其他实例和对象上下文本身。

·    因为 Include 返回查询对象,所以可以对一个 ObjectQuery<(Of <(<'T>)>)> 多次调用此方法,从而包括来自多个关系的实体,如: “context.Customers.Include("CustomerType").Include("CustomerEmails")”

·    如果使用查询路径,看似简单的对象查询也可能需要对数据源执行复杂的命令。 之所以发生这种情况,原因是在单个查询中返回相关对象要求具有一个或多个联接,这可能导致从数据源返回的每个相关实体出现冗余数据。 对复杂模型(如具有继承关系的实体或包含多对多关系的路径)进行的查询的复杂性将进一步加大。 使用 ToTraceString 方法可以查看将由 ObjectQuery<(Of <(<'T>)>)> 生成的命令。 如果查询路径包含过多相关对象,或对象包含过多行数据,数据源可能无法完成查询。 如果查询所需的中间临时存储区超过数据源的容量,则会出现这种情况。 出现这种情况时,通过显式加载相关对象或启用延迟加载可以降低数据源查询的复杂性。 如果在优化复杂查询后仍然频繁超时,请考虑通过设置 CommandTimeout 属性来增大超时值。

 

参考: MSDN 、《 Entity Framework Recipes

分享到:
评论

相关推荐

    Programming Entity Framework DbContext

    在本篇详细知识点讲解中,将基于给定文件信息,深入探讨Entity Framework(实体框架)中Code First方法的相关知识点。根据文件标题《Programming Entity Framework DbContext》和描述,该文件应该是关于Entity ...

    Entity Framework 学习资料

    Entity Framework (EF) 是微软提供的一种对象关系映射(ORM)框架,用于.NET应用程序中处理数据库交互。它允许开发者使用面向对象的编程语言(如C#或VB.NET)来操作数据库,而无需直接编写SQL语句,从而简化了数据...

    entityFramework源代码

    这个压缩包“entityFramework源代码”包含的是Entity Framework 6的源码,对于想要深入理解其工作原理和实现细节的开发者来说,这是一个宝贵的资源。 Entity Framework 6的主要特点包括: 1. **Code First**:这是...

    EntityFramework.dll

    EntityFramework.dll是微软开发的一个强大的对象关系映射(ORM)框架——Entity Framework的核心组件,主要用于.NET应用程序中。这个4.1版本的DLL文件是Entity Framework的早期版本,它为开发者提供了一个更简单的...

    Entity Framework 4.0 and Web Forms

    Entity Framework是微软的.NET框架的一部分,它是一个ORM(对象关系映射)框架,可以让我们在应用程序中使用对象的方式操作数据库。使用Entity Framework可以简化数据访问代码的编写,提高开发效率,降低出错的可能...

    Entity Framework官方中文教程

    Entity Framework(EF)是微软提供的一个对象关系映射(O/RM)框架,它简化了.NET开发人员访问数据库的代码编写,无需手动编写大量数据访问代码。EF允许开发者通过.NET对象模型来操作数据库。Entity Framework Core...

    EntityFrameworkCore.zip

    EntityFrameworkCore是一个强大的ORM(对象关系映射)框架,专为.NET Core和.NET Framework设计,由微软维护。它使得.NET开发者无需直接操作SQL语句,就能通过C#代码与数据库进行交互,极大地提高了开发效率。Entity...

    Entity Framework 学习

    Entity Framework(EF)是Microsoft开发的一个对象关系映射(ORM)框架,用于.NET应用程序,它允许开发者使用.NET语言(如C#或VB.NET)来操作数据库,而无需编写大量的SQL语句。通过EF,开发者可以将数据库操作抽象...

    EntityFramework4.1安装包

    Entity Framework 4.1 是微软开发的一个开源对象关系映射(ORM)框架,它为 .NET 开发者提供了强大的数据库访问能力。这个安装包解决了在 ADO.NET 框架下无法直接获取到 Entity Framework 4.1 的问题,使得开发者...

    EntityFramework.5.0.0dll文件

    EntityFramework.5.0.0.dll是微软开发的开源对象关系映射(ORM)框架Entity Framework的一个版本,主要用于简化.NET应用程序中的数据访问层操作。在这个版本中,它提供了丰富的功能和改进,帮助开发者更加高效地处理...

    .NET EntityFramework框架学习教程

    .NET Entity Framework 框架是微软提供的一种对象关系映射(ORM)工具,它允许开发者以面向对象的方式处理数据库操作,而无需直接编写SQL语句。这个框架在.NET Framework SP1版本中引入,旨在简化数据访问层的开发,...

    ADO.NET Entity Framework 增删改查

    实体框架 EntityFramework 使用概念层、映射层和逻辑层将逻辑数据库结构抽象化。 什么是 ADO.NET Entity Framework? ADO.NET Entity Framework 是一种实体框架,它提供了一种新的数据访问方式,允许开发人员使用...

    Entity Framework Core.pdf

    Entity Framework (EF) Core是微软开发的一个轻量级、可扩展且开源的对象关系映射(ORM)工具,专为.NET开发者设计,简化了数据库访问。它允许开发人员使用.NET对象来处理数据库,避免了大量的数据访问代码。EF Core...

    EntityFramework.dll 框架

    EntityFramework.dll 是一个重要的组件,它是微软开发的Object-Relational Mapping (ORM)框架,用于简化.NET应用程序中的数据库操作。ORM允许开发者使用面向对象的编程方式来处理数据库,避免了直接编写SQL语句,...

    Entity Framework学习初中高级篇-博客园cnblogs.rar

    Entity Framework(EF)是Microsoft开发的一个对象关系映射(ORM)框架,用于.NET应用程序,它允许开发者使用面向对象的编程语言(如C#或VB.NET)来操作数据库,而无需编写大量的SQL语句。这个“Entity Framework...

    Entity Framework 4.0 Recipes

    **Entity Framework (EF)** 是由微软开发的一款对象关系映射(Object-Relational Mapping, ORM)框架,它使得 .NET 开发者能够更轻松地在应用程序中操作数据库数据,而无需编写大量的 SQL 代码。**Entity Framework ...

    EntityFramework4.1复杂对象的保存

    标题中的“EntityFramework4.1复杂对象的保存”是指在使用Entity Framework 4.1时,如何处理并保存包含复杂类型的对象。Entity Framework (EF) 是一个对象关系映射(ORM)框架,允许开发人员使用.NET语言(如C#或VB...

    Entity Framework 6 Recipes, 2nd Edition.pdf

    ### Entity Framework 6 Recipes, 2nd Edition #### 关于书籍 《Entity Framework 6 Recipes, 2nd Edition》是一本全面介绍了如何使用Entity Framework 6进行数据访问和操作的技术书籍。本书通过一系列实用的解决...

    CVS to Entity Framework

    总结来说,【CVS to Entity Framework】涉及到了文件I/O、数据解析、对象建模、数据库迁移以及实体数据操作等多个方面,它是一个从CSV数据源到关系型数据库的有效桥梁,使数据处理更加便捷高效。在实际开发中,结合...

    Entity framework pdf

    Entity Framework(EF)是Microsoft开发的一个对象关系映射(ORM)框架,用于.NET应用程序。它允许开发者使用.NET语言(如C#或VB.NET)来操作数据库,而无需编写SQL语句,大大简化了数据访问层的开发。在这个...

Global site tag (gtag.js) - Google Analytics