`

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

分享到:
评论

相关推荐

    Entity Framework 6以模型方式搭建Sqlite数据库开发环境

    Entity Framework 6以模型方式搭建Sqlite数据库环境 Visual studio 2010 .net40 完成CURD 未处理 System.Configuration.ConfigurationErrorsException ... Source=System.Data ... Source=EntityFramework Line=0

    Entity Framework 6 Recipes(中文word翻译版)

    第25部分 加载实体和导航属性之加载完整的对象图和派生类型上的导航属性 第26部分 加载实体和导航属性之延缓加载关联实体和在别的LINQ查询操作中使用Include()方法 第27部分 加载实体和导航属性之关联实体过滤、排序...

    Entity Framework 6 实用精要

    微软官方提供的ORM工具,ORM让开发人员节省数据库访问的代码时间,将更多的时间放到业务逻辑层...EF提供变更跟踪、唯一性约束、惰性加载、查询事物等。开发人员使用Linq语言,对数据库操作如同操作Object对象一样省事。

    AutoFixture.AutoEntityFramework:一种AutoFixture定制,可在Entity Framework对象上延迟加载导航属性

    一种定制,它可以在Entity Framework对象上延迟加载导航属性。 初始化定制 该EntityCustomization构造函数需要一个实例IEntityTypesProvider 。 这告诉定制将哪些类型视为实体。 如果使用DbContext通过DbSet&lt;&gt;...

    EntityFramework基础

    EF提供变更跟踪、唯一性约束、惰性加载、查询事物等。开发人员使用Linq语言,对数据库操作如同操作Object对象一样省事。EF有三种使用场景,1.从数据库生成Class,2.由实体类生成数据库表结构,3.通过数据库可视化...

    Microsoft .NET Framework 4

    利用 Entity Framework,开发人员可使用 .NET 对象和语言集成查询 (LINQ) 对关系数据库进行编程。 它具有多项新功能,包括持久性忽略和 POCO 支持、外键关联、延迟加载、测试驱动开发支持、模型中的函数和新的 LINQ ...

    EntityLoaders:使用导航属性显式加载相关实体

    使用NuGet下载: 概述如果您使用Entity Framework,并且发现自己在使用急切加载和延迟加载的好处之间感到困惑,那么此项目可能正是您所需要的。 EntityLoaders提供了使用延迟加载的所有好处,而无需使代码库陷入...

    java 面试题 总结

    JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用...

    UnityGameFramework案例源码.zip

    资源 (Resource) – 为了保证玩家的体验,我们不推荐再使用同步的方式加载资源,由于 Game Framework 自身使用了一套完整的异步加载资源体系,因此只提供了异步加载资源的接口。不论简单的数据表、本地化字典,...

    超级有影响力霸气的Java面试题大全文档

    wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。 17、...

    asp.net知识库

    与DotNet数据对象结合的自定义数据对象设计 (一) 数据对象与DataRow ASP.NET中大结果集的分页[翻译] .net 2.0 访问Oracle --与Sql Server的差异,注意事项,常见异常 Ado.net 与NHibernate的关系? 动态创建数据库...

    framword4.5

    利用 Entity Framework,开发人员可使用 .NET 对象和语言集成查询 (LINQ) 对关系数据库进行编程。 它具有多项新功能,包括持久性忽略和 POCO 支持、外键关联、延迟加载、测试驱动开发支持、模型中的函数和新的 LINQ ...

    ssh(structs,spring,hibernate)框架中的上传下载

     需要指定的是Spring 1.2.5提供了两套Hibernate的支持包,其中Hibernate 2相关的封装类位于org.springframework.orm.hibernate2.*包中,而Hibernate 3.0的封装类位于org.springframework.orm.hibernate3.*包中,...

    客户关系管理系统框架搭建(二)

    &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt; &lt;!--表示在类路径下加载hibernate.cfg.xml --&gt; &lt;value&gt;classpath:hibernate.cfg.xml ...

    Visual.Basic.2010.&.NET4.高级编程(第6版)-文字版.pdf

    第11章 使用entity framework访问数据 441 11.1 对象关系映射 441 11.2 entity framework体系结构 442 11.2.1 概念模型 443 11.2.2 存储模型 446 11.2.3 映射模型 447 11.2.4 linq to entities 448 ...

Global site tag (gtag.js) - Google Analytics