在使用Entity Framework加载关联实体时,可以有三种方式:

1.懒加载(lazy Loading);

2.贪婪加载(eager loading);

3.显示加载(explicit loading)。

EF默认使用的是懒加载(lazy Loading)。一切由EF自动处理。

这种方式会导致应用程序多次连接数据库,这种情况推荐在数据量较大的情况下使用。当我们需要加载数据较少时,一次性全部加载数据会相对更高效。

我们来看看EF的显示加载(explicit loading)如何让我们完全掌控数据的加载(非自动化)。

这里我们使用Code First模式。

数据表:商品表,商品分类表,品牌表

Step1:新建控制台应用程序,添加EF6引用(可以使用NuGet获取)

Step2:创建三个实体对象:Product、Category 、Brand

Product实体类:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace EFExplicitLoading.Models {
public class Product {
public int ProductId { get; set; }
public String Title { get; set; }
public int CategoryId { get; set; } public virtual Category Category { get; set; } public virtual Brand Brand { get; set; } }
}

Category实体类:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace EFExplicitLoading.Models {
public class Category { public Category() {
Products = new HashSet<Product>();
} public int CategoryId { get; set; }
public String Name { get; set; } public virtual ICollection<Product> Products { get; set; }
}
}

Brand实体类:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace EFExplicitLoading.Models {
public class Brand { public Brand() {
Products = new HashSet<Product>();
} public int BrandId { get; set; } public String Name { get; set; } public virtual ICollection<Product> Products { get; set; }
}
}

Step3:创建派生自DbContext的类(ExplicitContext)

 using System.Data.Entity;
using EFExplicitLoading.Models; namespace EFExplicitLoading { public class ExplicitContext : DbContext {
public ExplicitContext()
: base("ExplicitConnectionString") {
} public DbSet<Product> Products { get; set; } public DbSet<Category> Categories { get; set; } public DbSet<Brand> Brands { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Product>().ToTable("Product");
modelBuilder.Entity<Brand>().ToTable("Brand");
modelBuilder.Entity<Category>().ToTable("Category");
}
} }

Step4:在App.config中添加connectionStrings节点

   <connectionStrings>
<add name="ExplicitConnectionString" connectionString="Data Source=.;Initial Catalog=EFExplicit;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
</connectionStrings>

准备工作已就绪,当我们使用ExplicitContext类时,EF会根据实体自动创建数据库和表

下面回到控制台入口函数处:

Step5:添加测试数据

             using (var context = new ExplicitContext()) {
//先清空数据
context.Database.ExecuteSqlCommand("delete from product");
context.Database.ExecuteSqlCommand("delete from brand");
context.Database.ExecuteSqlCommand("delete from category"); var category1 = new Category {Name = "服装"};
var category2 = new Category {Name = "家电"};
var brand1 = new Brand {Name = "品牌1"};
var brand2 = new Brand {Name = "品牌2"}; context.Products.Add(new Product {Brand = brand1, Category = category1, Title = "产品1"});
context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "产品2"});
context.Products.Add(new Product {Brand = brand1, Category = category2, Title = "产品3"});
context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品4"});
context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品5"});
context.Products.Add(new Product {Brand = brand2, Category = category2, Title = "产品6"});
context.SaveChanges();
}

Step6:开始测试explicit loading

 using (var context = new ExplicitContext()) {
//禁用懒加载
context.Configuration.LazyLoadingEnabled = false; var category = context.Categories.First(c => c.Name == "家电"); //判断分类关联的产品是否已经被加载
if (!context.Entry(category).Collection(x => x.Products).IsLoaded) {
context.Entry(category).Collection(x => x.Products).Load();
Console.WriteLine("分类{0}的产品被显示加载...", category.Name);
} Console.Write("分类{0}下的共有{1}件产品", category.Name, category.Products.Count()); foreach (var product in context.Products) {
if (!context.Entry(product).Reference(x => x.Category).IsLoaded) {
context.Entry(product).Reference(x => x.Category).Load();
Console.WriteLine("分类{0}被显示加载.", product.Category.Name);
}
else {
Console.WriteLine("分类{0}已经加载过了.", product.Category.Name);
}
} //重新使用category计算 产品数量
Console.WriteLine("产品加载完后,分类{0}下有{1}件产品", category.Name, category.Products.Count()); category.Products.Clear();
Console.WriteLine("产品集合被清空了.");
Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count()); context.Entry(category).Collection(x => x.Products).Load();
Console.WriteLine("重新显示加载产品");
Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count()); //使用ObjectContext刷新实体
var objectContext = ((IObjectContextAdapter) context).ObjectContext;
var objectSet = objectContext.CreateObjectSet<Product>();
objectSet.MergeOption = MergeOption.OverwriteChanges;
objectSet.Load();
//MergeOption.AppendOnly
//MergeOption.OverwriteChanges
//MergeOption.NoTracking
//MergeOption.PreserveChanges Console.WriteLine("产品集合以MergeOption.OverwriteChanges的方式重新加载");
Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
} Console.WriteLine("测试部分加载...");
//测试部分加载,然后加载所有
using (var context = new ExplicitContext()) {
//禁用懒加载
context.Configuration.LazyLoadingEnabled = false;
var category = context.Categories.First(c => c.Name == "家电");
//先加载1条数据
Console.WriteLine("先加载1条数据");
context.Entry(category).Collection(x => x.Products).Query().Take().Load();
Console.WriteLine("分类{0}下共有{1}件产品", category.Name, category.Products.Count()); //加载所有
context.Entry(category).Collection(x => x.Products).Load();
Console.WriteLine("加载所有数据"); Console.WriteLine("此时分类{0}下有{1}件产品", category.Name, category.Products.Count());
} Console.ReadKey();

执行结果:

禁用懒加载(lazy loading)

要想测试我们的explict loading,必须先禁用懒加载,有2种方式可以禁用懒加载:

1.设置Context.Configuration.LazyLoadingEnabled的值为false   或

2.移除每一个实体类中的关联实体属性(导航属性)的virtual访问修饰符,这种方法可以更精确控制懒加载,即只禁用移除virtual修饰符实体的懒加载

IsLoaded属性和Load方法

接下来,我们利用EF获取一条Name为家电的分类实体数据,由于Category与Product是一对多的关系,此时我们可以使用IsLoaded属性来测试此分类下的Products集合是否被加载

context.Entry(category).Collection(x => x.Products).IsLoaded

若没有加载,我们使用Load方法来加载关联的Products,在接下来的foreach遍历中,我们可以看到,Category关联的Product已经全部被加载完毕

PS:当使用Take(n)方法Load数据时,IsLoaded属性依然为False,因为只有当关联的所有数据加载完,IsLoaded才为true

关系修复(relationship fixup)

这里Entity Framework使用称为"关系修复"的技术(relationship fixup),即当我们单独加载关联实体的时候(Product),这些数据会自动挂载到已经载入的实体(Category),但这种关系修复并不是总是会生效,比如在多对多的关系中就不会这样处理。

关于Clear()

然后我们打印出来Category中有多少Product,之后使用Clear()方法清空category对象的关联集合Products。

Clear方法会移除Category和Product的关联关系,但Product集合数据并没有删除,依然存在上下文的内存中,只是它不再与Category关联。

后面我们又重新使用Load加载Product但category.Products.Count()的值依然为0。这正好说明Clear方法的本质。

这是因为Load默认使用MergeOption.AppendOnly的方式加载数据,找不到Product实例了(被Clear了)。然后我们使用MergeOption.OverwriteChanges的方式才将数据重新关联

关于MergeOption(实体合并选项)

MergeOption枚举共有4个选项

1.MergeOption.AppendOnly

  在现有的关系基础上合并
2.MergeOption.OverwriteChanges

  OverwriteChanges模式,会从数据库中更新当前上下文实例的值(使用同一个实体对象的实例,如category)。

  当你想要恢复实体在上下文的改变,从数据库刷新实体时,使用这个模式就非常有用。

3.MergeOption.NoTracking

  无追踪模式不会跟踪对象的变化,也不会意识到对象已经被加载到当前上下文

  NoTracking可以应用到一个实体的导航属性(关联实体属性),但这个实体也必须使用NoTracking

  反过来,NoTracking应用到某个实体时,这个实体的导航属性会忽略默认的AppendOnly模式而使用NoTracking模式
4.MergeOption.PreserveChanges

  PreserveChanges选项本质上与OverwriteChanges选项相反,

  PreserveChanges模式会更新查询结果集(若数据库中有变化),但不会更新在当前上下文内存中的值

PS: 也许你已经注意到,这里我们使用了ObjectContext对象,而不是context对象,这是因为DbContext不能直接使用MergeOption类型,所以必须使用ObjectContext对象

关于性能提升

在任何时候,关联实体集合的数量被限制时,使用Load方法,会有助于我们提升程序的性能,比如加载一个Category中的5条Product。

在少量场景中,我们需要整个关联集合时,也可以使用Load

PS:当一个实体在Added(添加)、Deleted(删除)、Detected(分离)状态时,Load方法无法使用。

关于Entity Framework性能提升的方法还有很多,当然,我们必须根据自己的项目实际情况来做优化。不同的应用场景,选择不同的优化方案

示例代码:点此下载

原文链接:http://www.cnblogs.com/jayshsoft/p/entity-framework-explicit-loading.html

Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)的更多相关文章

  1. Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)

    上篇中"Entity Framework中的Identity map和Unit of Work模式", 由于EF中的Identity map和Unit of Work模式,EF体现 ...

  2. 分享基于Entity Framework的Repository模式设计(附源码)

    关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...

  3. Entity Framework Tutorial Basics(38):Explicit Loading

    Explicit Loading with DBContext Even with lazy loading disabled, it is still possible to lazily load ...

  4. 使用MiniProfiler给Asp.net MVC和Entity Framework号脉(附源码)

    在学习python开发框架pylons/pyramid的过程中,里面有个非常棒的页面性能监控功能,这样在开发过程中,你能清楚的知道当前页面的性能以及其它参数. 这里介绍一下如何给Asp.net MVC ...

  5. Entity Framework Tutorial Basics(37):Lazy Loading

    Lazy Loading: One of the important functions of Entity Framework is lazy loading. Lazy loading means ...

  6. Entity Framework Tutorial Basics(36):Eager Loading

    Eager Loading: Eager loading is the process whereby a query for one type of entity also loads relate ...

  7. 分享一个Winform里面的HTML编辑控件Zeta HTML Edit Control,汉化附源码

    我们知道,Web开发上有很多HTML的编辑控件,如FCKEditor.CKEditor.kindeditor等等,很多都做的很好,而虽然Winform里面有WebBrowser控件,但是默认这个控件是 ...

  8. Entity Framework中的Identity map和Unit of Work模式

    阅读目录: 一.什么是Identity map模式 二.关于Identity map模式的验证示例 三.Unit of Work 模式 四.总结和注意的问题 一,什么是Identity map模式 I ...

  9. Entity Framework中的Identity map和Unit of Work模式(转)

    一,什么是Identity map模式 Identity map是EF获取和缓存数据的模式.Identity map模式指的是任何数据都只会被加载一次,以map的形式缓存,以唯一的identity来再 ...

随机推荐

  1. Android 开源框架ActionBarSherlock 和 ViewPager 仿网易新闻客户端

    转载请注明出处:http://blog.csdn.net/xiaanming/article/details/9971721 大家都知道Android的ActionBar是在3.0以上才有的,那么在3 ...

  2. ORACLE 主要后台进程1

    Oracle Database Background Processes: 1.Database writer (DBWn)The database writer writes modified bl ...

  3. 从一个非开发人员转行silverlight满一年的工作总结(第一次发帖)

    自2013年3月进入公司到现在已整整一年.这一年,让我从一个大学毕业就去参军并且专业还不对口的大学生步入了软件开发这个高门槛行业.说实话,我真的很庆幸,庆幸遇到了两位赏识自己的领导从很多专业对口.能力 ...

  4. 一点用JS写控制权限的心得

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  5. UVA 1374 Power Calculus

    题意: 给出m,问对n最少进行几次操作.n初始为1,能得到m.操作1位将n平方.操作2为将n除以之前出现的n值中的任意一个. 分析: 其实是关于指数的操作,即从1到m最少的步数.我们可以先确定最少步数 ...

  6. 详解Activity的四种启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. Ac ...

  7. linux(Ubuntu)下mysql字符集完美解决

    本文参考: 1. http://www.blogjava.net/wldandan/archive/2007/09/04/142669.html 2. http://chinaapp.sinaapp. ...

  8. 修改已经提交到远端的git commit信息

    有这么一种场景,就是以前没有设置用户名和邮箱,导致提交时git commit信息中用户信息不正确.这样的情况导致后来我们提交代码到git上面时因为身份验证错误,只有到 push 阶段了才发现提交不上去 ...

  9. MySQL必知必会笔记<2>

    [英]ben Forta著 5 1.0  *使用扩展查询* |---->select note from table   where Match(note) Against('anl'); |- ...

  10. codeforces 702E Analysis of Pathes in Functional Graph 倍增

    题目链接 给一个图, 然后给出每条边的权值和一个k值. 让你求出从每个点出发, 走k次能获得的边权的和以及边权的最小值. 用倍增的思想, 求出每个点走一次能到达的点, 权值和以及最小值, 走两次..四 ...