在使用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. netty demo

    Netty 4.0 demo   netty是一个异步,事件驱动的网络编程框架&工具,使用netty,可以快速开发从可维护,高性能的协议服务和客户端应用.是一个继mina之后,一个非常受欢迎的 ...

  2. RDLC报表系列(二) 行分组

    接上一篇文章的内容,今天来说的是行分组.还是打开demo1.rdlc界面,拖入一个文本框和表 1.在表中随便选择一个字段,不然在添加行组的时候不会自动提示.我这里是选择的Dept 2.在下面的行组中右 ...

  3. [Effective C++系列]-透彻了解inlining的里里外外

    Understand the ins and outs of inlining.   [原理] Inline函数背后的做法是将“对函数的每一个调用”都用函数本体(function body)替换之.其 ...

  4. php 注释

    @access 使用范围:class,function,var,define,module 该标记用于指明关键字的存取权限:private.public或proteced @author 指明作者 @ ...

  5. 关于在页面上执行sql语句

    在页面上执行sql语句,首先在页面上顶一个文本域,让用户输入需要执行的sql语句. html代码如下: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML ...

  6. CI的扩展机制

    CI的扩展机制 在熟悉了CI的源码之后,它的简单明了的代码风格很有趣,这篇文章看看在CI是如何实现扩展的. 扩展包 扩展是为了完成特定的功能,在CI中,扩展包的开发只能在application/lib ...

  7. cordova开发中遇到的一些坑

    # 外域的JS被禁止,修改meta标签 <meta http-equiv="Content-Security-Policy" content="default-sr ...

  8. JSC学习笔记:JavaScriptCore 初识

    JSContext/JSValue JSContext是运行JavaScript的上下文环境,是一个全局环境实例:类似于浏览器端的window对象,表示浏览器的窗口:在浏览器中,所有JavaScrip ...

  9. centos无法载入 mcrypt 扩展,<br />请检查 PHP 配置,经过各种尝试,终于找到了解决办法

    百度了无数个方法都没有解决问题,也是折腾死我了,最终解决了问题 解决办法:安装php-mcrypt libmcrypt libmcrypt-devel这三个库文件 1.安装第三方yum源(默认yum源 ...

  10. Three-Color Flag

    问题陈述: 三色旗的问题最早由E.W.Dijkstra所提出,他所使用的用语为Dutch Nation Flag(Dijkstra为荷兰人),而多数的作者则使用Three-Color Flag来称之. ...