在使用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. Jam's math problem(思维)

    Jam's math problem Submit Status Practice HDU 5615   Description Jam has a math problem. He just lea ...

  2. 步步学LINQ to SQL:使用LINQ检索数据【转】

    [IT168 专稿]该系列教程描述了如何采用手动的方式映射你的对象类到数据表(而不是使用象SqlMetal这样的自动化工具)以便能够支持数据表之间的M:M关系和使用实体类的数据绑定.即使你选择使用了自 ...

  3. 图数据库之Pregel

    /* 版权声明:能够随意转载,转载时请务必标明文章原始出处和作者信息 .*/            author: 张俊林 节选自<大数据日知录:架构与算法>十四章.书籍文件夹在此 Pre ...

  4. 好用的DNS服务器推荐

    DNS在平时上网中扮演重要角色,如果不注意DNS的话,可能会导致网速慢.弹窗广告.网址打不开.打开不是自己想要的网站.淘宝客劫持等一系列问题.针对DNS的问题,网络上也有各种DNS平台供用户选择.这里 ...

  5. JS获取客户端IP地址、MAC和主机名七种方法

    一.使用JS获取客户端IP的几个方法方法一(只针对IE且客户端的IE允许AcitiveX运行,通过平台:XP,SERVER03,2000).获取客户端IP代码:<HTML><HEAD ...

  6. VMware虚拟机与主机联通及配置上网

    vmware版本:10.0.0 build-1295980,安装redhat enterprise linux 5.8 一.物理机与虚拟机联通,但不联网 1.虚拟网络VMnet1设置: 此时,物理主机 ...

  7. JavaScript事件处理程序 学习笔记

    我一直认为Javascript的特点就是在和用户交互的过程中可以进行一些操作,那么事件作为用户交互的主要部分就显得特别重要,今天先学习了JS事件处理程序的相关内容. 首先,要明白Javascript ...

  8. .net程序员转战android第一篇---环境部署

    对于.net开发人员去写java,可谓说是见山是山, 因为太多的相同; 最近段时间因工作因素,将项目中部分功能需要移植到android平台上,经过半个月的煎熬,终于搞完了. 文章中将直观记录我做项目中 ...

  9. 【IOS学习基础】OC类的相关

    几天前突然在别人的类的.m文件中看到这么一句代码:@synthesize xxxx = _xxxx; 当时愣是没理解啥意思,过后才缓过神来发现原来是把一些类的基础知识忘记了,虽然不用过多去深究以前的一 ...

  10. 读取hdfs文件内容

    基础环境: cdh2.71 需要注意: url地址参照 <property> <name>dfs.namenode.servicerpc-address</name> ...