Code First Entity Framework 6化被动为主动之explicit loading模式实战分析( 附源码)
在使用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模式实战分析( 附源码)的更多相关文章
- Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)
上篇中"Entity Framework中的Identity map和Unit of Work模式", 由于EF中的Identity map和Unit of Work模式,EF体现 ...
- 分享基于Entity Framework的Repository模式设计(附源码)
关于Repository模式,在这篇文章中有介绍,Entity Framework返回IEnumerable还是IQueryable? 这篇文章介绍的是使用Entity Framework实现的Rep ...
- Entity Framework Tutorial Basics(38):Explicit Loading
Explicit Loading with DBContext Even with lazy loading disabled, it is still possible to lazily load ...
- 使用MiniProfiler给Asp.net MVC和Entity Framework号脉(附源码)
在学习python开发框架pylons/pyramid的过程中,里面有个非常棒的页面性能监控功能,这样在开发过程中,你能清楚的知道当前页面的性能以及其它参数. 这里介绍一下如何给Asp.net MVC ...
- Entity Framework Tutorial Basics(37):Lazy Loading
Lazy Loading: One of the important functions of Entity Framework is lazy loading. Lazy loading means ...
- 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 ...
- 分享一个Winform里面的HTML编辑控件Zeta HTML Edit Control,汉化附源码
我们知道,Web开发上有很多HTML的编辑控件,如FCKEditor.CKEditor.kindeditor等等,很多都做的很好,而虽然Winform里面有WebBrowser控件,但是默认这个控件是 ...
- Entity Framework中的Identity map和Unit of Work模式
阅读目录: 一.什么是Identity map模式 二.关于Identity map模式的验证示例 三.Unit of Work 模式 四.总结和注意的问题 一,什么是Identity map模式 I ...
- Entity Framework中的Identity map和Unit of Work模式(转)
一,什么是Identity map模式 Identity map是EF获取和缓存数据的模式.Identity map模式指的是任何数据都只会被加载一次,以map的形式缓存,以唯一的identity来再 ...
随机推荐
- flume-采集报错
h2 { color: #fff; background-color: #7CCD7C; padding: 3px; margin: 10px 0px } h3 { color: #fff; back ...
- 无组件客户端js图片压缩
<div class="free-upload"> <p>上传您的约会照片,一张合影.一张票据哦!</p> <div class=&quo ...
- Baby Ming and Matrix games(dfs计算表达式)
Baby Ming and Matrix games Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Ja ...
- C# 整个网页保存成图片
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- Linux 重定向
Linux 标准文件描述符 描述符 缩写 描述 0 STDIN 标准输入 1 STDOUT 标准输出 2 STDERR 标准错误 3-9 应该是扩展的标准输出(待验证) 命令行重定 ...
- L9-1-安装Apache
一.安装Apache服务器: 安装apr [root@OCP soft]# tar -zxf apr-1.5.1.tar.gz [root@OCP apr-1.5.1]# ./configure -- ...
- 解決 IE10 浏览器无法使用 ASP.NET From 验证登录的问题
最近应项目用到ASP.Net表单验证机制(FormsAuthentication),来判断用户是否已经登录,一切测试顺利,最后发布到IIS中后在IE10测试是发现始终判断用户没登录(其他浏览器一切正常 ...
- KZ--NSString、NSMutableString
//NSString初始化的几种方法(3种方法) //1. NSString *str2 = [[NSString alloc] init]; ...
- HTML5 canvas绘制线条曲线
HTML5 canvas入门 线条例子 1.简单线条 2.三角形 3.填充三角形背景颜色 4.线条颜色以及线条大小 5.二次贝塞尔曲线 6.三次贝塞尔曲线 <!doctype html> ...
- Amazon MWS 上传数据 (二) 构造请求
上一篇文章提到了Amazon 上传数据有三个步骤,但是每个步骤都需要构造服务和构造请求,服务是一样的,请求各不相同:这个很容易理解,这三个步骤都需要和Amazon服务器交互,所以他们的服务构造是一样的 ...