Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高

问题

你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现.

解决方案

假设你有一个关于Account(帐户)和相关的Payments(支付)的模型,如Figure 13-7

Figure 13-7. A model with an Account entity and a related Payment

 

首先,本例用EF的CodeFirst方式实现,在Listing 13-16,我们创建实体类:Account和Payment.为达到最优化的修改跟踪性能,我们需要允许EF能用修改跟踪代理类自动地封装我们的实体类,代理能立即通知底层的代理跟踪机构能随时跟踪属性的修改,使用代理,EF一直都能知道你的实体的state(状态).当创建代理时,通知事件会被添加到每个属性的setter方法上,这个过程由Object State Manager(对象状态管理器)完成.当达到以下两个条件时EF会立即创建一个代理类:(1)所有的实体的属性都必须是virtual,(2)任何集合类的导航属性类型必须是Icollection<T>.EF会override满足这两个条件的实体类,并添加必要的修改跟踪装置.

我们的Account和Payment实体类符合这两件条件,如Listing 13-16 所示:

Listing 13-16. Our Entity Classes with Properties Marked as virtual and the Navigation Properties Are of Type

ICollection<T>

public class Account

{

public virtual int AccountId { get; set; }

public virtual string Name { get; set; }

public virtual decimal Balance { get; set; }

public virtual ICollection<Payment> Payments { get; set; }

}

public class Payment

{

public virtual int PaymentId { get; set; }

public virtual string PaidTo { get; set; }

public virtual decimal Paid { get; set; }

public virtual int AccountId { get; set; }

}

接下来,在Listing 13-17 我们创建用CodeFirst方式访问EF功能的途径,DbContext对象

Listing 13-17. DbContext Object

public class Recipe5Context : DbContext

{

public Recipe5Context()

: base("Recipe5ConnectionString")

{

// Disable Entity Framework Model Compatibility

Database.SetInitializer<Recipe6Context>(null);

}

protected override void OnModelCreating(DbModelBuilder modelBuilder)

{

modelBuilder.Entity<Account>().ToTable("Chapter13.Account");

modelBuilder.Entity<Payment>().ToTable("Chapter13.Payment");

}

public DbSet<Account> Accounts { get; set; }

public DbSet<Payment> Payments { get; set; }

}

接下来在项目中添加App.config类,并把Listing 13-18 的代码添加到ConnectionStrings节下

Listing 13-18. Connection String

<connectionStrings>

<add name="Recipe5ConnectionString"

connectionString="Data Source=.;

Initial Catalog=EFRecipes;

Integrated Security=True;

MultipleActiveResultSets=True"

providerName="System.Data.SqlClient" />

</connectionStrings>

接下来Listing 13-19 演示了插入,获取,和更新我们的模型.

Listing 13-19. Inserting, Retrieving, and Updating Our Model

using (var context = new Recipe5Context())

{

var watch = new Stopwatch();

watch.Start();

for (var i = 0; i < 5000; i++)

{

var account = new Account

{

Name = "Test" + i,

Balance = 10M,

Payments = new Collection<Payment> { new Payment { PaidTo = "Test" + (i + 1), Paid = 5M } },

};

context.Accounts.Add(account);

Console.WriteLine("Adding Account {0}", i);

}

context.SaveChanges();

watch.Stop();

Console.WriteLine("Time to insert: {0} seconds", watch.Elapsed.TotalSeconds.ToString());

}

using (var context = new Recipe5Context())

{

var watch = new Stopwatch();

watch.Start();

var accounts = context.Accounts.Include("Payments").ToList();

watch.Stop();

Console.WriteLine("Time to read: {0} seconds", watch.Elapsed.TotalSeconds.ToString());

watch.Restart();

foreach (var account in accounts)

{

account.Balance += 10M;

account.Payments.First().Paid += 1M;

}

context.SaveChanges();

watch.Stop();

Console.WriteLine("Time to update: {0} seconds", watch.Elapsed.TotalSeconds.ToString());

}

它是如何工作的

EF的最后一个版本,包含6.0,我们用POCO类来代表实体类,POCO是Plain Old CRL Object缩写,通常只包含与数据库表列对应的态和属性.POCO类没有依赖.NET CLR基类,尤其没有依赖EF的东西.

对POCO实体类的修改跟踪,要么使用快照,要么使用代理类,使用快照的方式,EF先拍张照,打个比方说,一个实体的数据值就是从一个查询后被装载入上下文时的值或一个Attach()操作.在一个SaveChanges()操作前,用原始的快照与当前数据值比对来检测是否发生过修改.用这种方式,EF维护每个对象的两个拷贝并比较它们,然后生成对应的SQL更新,插入,和删除语句.你可能会认为这种方式会比较慢,但EF在比较不同时是非常快的.

■■注意  上下文的Add()操作不会对添加的实体进行拍照,因为实体是新的,没有必要对单个的值进行修改跟踪.EF会把该实体标志为Added,在SaveChanges()操作时,会产生对应的插入SQL语句.

第二个方式,如Listing 13-19描述的,用一个实现了IentityWithChangeTracking接口的代理对象来包装底层的实体POCO对象.该代理负责通知Object State Manager(对象状态管理器)实体对象的属性和相关联对象属性的修改.EF会自动为你的POCO对象创建代理,当你把在你的POCO类里把每个属性标为virtual,把每个导航属性返回Icollection<T>类型.代理避免了可能复杂的快照方式下的对象与对象之间的比较,虽然代理也需要为跟踪每个变化的发生付出些负载.

尽管修改跟踪代理立即通知修改跟踪组件对象的修改并避免对象的比较,在实践中,性能上所带来的好处也只在模型非常复杂并只有少量的修改时,才能体现出来.Figure 13-7所示的模型非常简单,如果你改变Listing 13-19的代码,用快照的方式,你可能会注意到用代理类只快一两秒.

注意 代理类在n层结构,并且需要序列化数据并传送给另一个层(比如传一个WCF或WEB API客户端,更多查看Recipe 9-7)时,会变得比较麻烦.

时间: 2024-10-07 04:19:56

Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高的相关文章

Entity Framework 6 Recipes 2nd Edition(9-3)译-&gt;找出Web API中发生了什么变化

9-3. 找出Web API中发生了什么变化 问题 想通过基于REST的Web API服务对数据库进行插入,删除和修改对象图,而不必为每个实体类编写单独的更新方法. 此外, 用EF6的Code Frist实现数据访问管理. 本例,我们模拟一个N层场景,用单独的客户端(控制台应用)来调用单独的基于REST服务的Web网站(WEB API应用) . 注意:每层使用单独的Visual Studio 解决方案, 这样更方便配置.调试和模拟一个N层应用. 假设有一个如Figure 9-3所示的旅行社和预订

Entity Framework 6 Recipes 2nd Edition(13-3)译 -&gt; 为一个只读的访问获取实体

问题 你想有效地获取只是用来显示不会更新的操作的实体.另外,你想用CodeFirst的方式来实现 解决方案 一个非常常见行为,尤其是网站,就是只是让用户浏览数据.大多数情况下,用户不会更新数据.在这种情况下,你可以通过避开上下文的缓存和修改跟踪来提高代码性能,你可以非常简单地使用AsNoTracking方法来实现. 让我们假设你一个应用程序来管理doctor(医生)的appointments(预约),你的模型如下图Figure 13-5. Figure 13-5. A model for man

Entity Framework 6 Recipes 2nd Edition(13-4)译 -&gt; 有效地创建一个搜索查询

问题 你想用LINQ写一个搜索查询,能被转换成更有效率的SQL.另外,你想用EF的CodeFirst方式实现. 解决方案 假设你有如下Figure 13-6所示的模型 Figure 13-6. A simple model with a Reservation entity            首先,这个例子用EF的CodeFirst方式实现,在Listing 13-10,我们创建实体类Reservation Listing 13-10. The Reservation Entity Obje

Entity Framework 6 Recipes 2nd Edition(13-2)译 -&gt; 用实体键获取一个单独的实体

问题 不管你用DBFirst,ModelFirst或是CodeFirst的方式,你想用实体键获取一个单独的实体.在本例中,我们用CodeFirst的方式. 解决方案 假设你有一个模型表示一个Painting(绘画)类型的实体,如Figure 13-2所示: Figure 13-2. The Painting entity type in our model 在代码In Listing 13-2,我们创建实体类Painting. public class Painting { public str

Entity Framework 6 Recipes 2nd Edition(9-4)译-&gt;Web API 的客户端实现修改跟踪

9-4. Web API 的客户端实现修改跟踪 问题 我们想通过客户端更新实体类,调用基于REST的Web API 服务实现把一个对象图的插入.删除和修改等数据库操作.此外, 我们想通过EF6的Code First方式实现对数据的访问. 本例,我们模拟一个N层场景,用单独的控制台应用程序作为客户端,调用Web API服务(web api项目). 注:每个层用一个单独的解决方案,这样有助于调试和模拟N层应用. 解决方案 假设我们一个如Figure 9-4.所示模型 Figure 9-4. A 客户

Entity Framework 6 Recipes 2nd Edition(9-1)译-&gt;用Web Api更新单独分离的实体

第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程,和数据层,并且它们可能分布在多台计算机上,被分布到一台单独的计算机上的应用程序的某个领域的逻辑层,并不过多地涉及代理服务器编码,序列化,和网络协议,应用程序可以跨越很多设备,从小到一个移动设备到大到一个包含企业所有账户信息的数据服务器. 幸运的是,EF可应用于WCF,WEB Api等诸如此类的多层框

Entity Framework 6 Recipes 2nd Edition(目录索引)

Chapter01. Getting Started with Entity Framework / 实体框架入门 1-1. A Brief Tour of the Entity Framework World / 简单浏览实体框架世界 goto1-2. Using Entity Framework / 使用实体框架 Chapter02. Entity Data Modeling Fundamentals / 实体数据建模基础 2-1. Creating a Simple Model2-2. C

Entity Framework 6 Recipes 2nd Edition(11-2)译 -&gt; 为一个”模型定义”函数返回一个计算列

11-3. 为一个”模型定义”函数返回一个计算列 问题 想从”模型定义”函数里返回一个计算列 解决方案 假设我们有一个员工(Employee)实体,属性有: FirstName, LastName,和BirthDate, 如 Figure 11-3所示. Figure 11-3. An Employee entity with a few typical properties 我们想要创建一个”模型定义”函数,让它返回FirstName 和LastName 合并后的full name . 我们想

Entity Framework 6 Recipes 2nd Edition(11-9)译 -&gt; 在LINQ中使用规范函数

11-9. 在LINQ中使用规范函数 问题 想在一个LINQ查询中使用规范函数 解决方案 假设我们已经有一个影片租赁(MovieRental )实体,它保存某个影片什么时候租出及还回来,以及滞纳金等,如Figure 11-9. 所示: Figure 11-9. The MovieRental entity that has the dates for a rental period along with any late fees 我们想取得所有租期超过10天的影片 如何创建和使用查询,如Lis