EF的关联实体加载有三种方式
- Lazy Loading
- Eager Loading
- Explicit Loading
*其中Lazy Loading和Explicit Loading都是延迟加载。
(一)延迟加载(Lazy Loading:)(默认)1:
1.使用的是动态代理
2.默认情况下,如果POCO类满足以下两个条件,EF就使用延迟加载
- POCO类是Public且不为Sealed。
- 导航属性标记为Virtual。
3.关闭Lazy Loading - 可以将LazyLoadingEnabled设为false
- 如果导航属性没有标记为virtual,Lazy Loading也是不起作用的。
(二)贪婪加载(Eager Loading):
不设置导航属性为virtual,并且对导航属性使用Include,Eager Loading使用Include方法关联预先加载的实体。
(三)显式加载(Explicit Loading):
1.不设置导航属性为virtual
2.导航属性使用Reference(单个对象).Load()或Collection(对象集).Load()。
- 显式加载使用Entry方法,对于集合使用Collection,单个实体则使用Reference(详细看下文)
Lazy Loading
又称惰性加载,懒加载
示例代码:
using (var dbcontext= new ModelFirstDemoEntities())
{
#region 延迟加载:用的时候加载
var customers = from c in dbcontext.Customer
select c;
foreach (var cus in customers)
{
Console.WriteLine(cus.Id);
foreach (var order in cus.Order) //根据导航属性,自动查询客户的订单
{
Console.WriteLine(order.OrderContent);
}
}
#endregion
}
以上代码中,首先会执行一次查询,并将返回的结果存放在变量customers 中。而foreach循环会在每一次循环过程中独立进行一次查询,所以,如果数据库表Customer中有100条记录,那么整个过程将产生101次查询
下图是程序执行的结果以及在SQL Server Profiler中的跟踪记录。可以清楚地看到,对表Customer只进行了一次查询,由于该表只有7条记录,于是在循环中又分别产生了7次对表Order的查询
此处如果如果将数据库上下文的属性设置为 false 的话(Configuration.LazyLoadingEnabled = false; )将不会查询到从表的数据,只会执行一次查询—而这,也就是下文中的贪婪加载
延迟加载异常
异常提示:若在Main方法中,接收以下函数的值,并视图获取City数据时。会提示此 ObjectContext 实例已释放,不可再用于需要连接的操作。
英文提示为:“the ObjectContext instance has been disposed and can no longer be used for operations that require a connection”
using (var db = new SignUpModelContainer()) {
var oldEntity = db.SignUpRequests
.Include("ApproveRemarkList.ApproveUser")
.Include("SchoolSetting.School")
.Include("Province")
.Include("City")
.Include("District")
.FirstOrDefault(s => s.ID == id); return oldEntity;
}
原因:
因为此处使用的模式是延迟加载,而在using的范围结束后,外部通过oldEntity继续获取关联子属性的数据时,因为container已经关闭,不能通过容器并延迟加载获取数据
解决方案:
1.关闭延迟加载并使用include进行表间关联
2.使用toList
此 ObjectContext 实例已释放,不可再用于需要连接的操作。
关闭延迟加载
1.外键属性更改
- 关闭特定的外键属性
将Enrollment类声明称public ICollection Enrollment {get;set;}
原代码
public virtual ICollection<Enrollment> Enrollment {get;set;}
2.关闭所有属性的懒加载,在DbContext中声明this.Configuration.LazyLoading = false;
在entities的构造函数中,设定关闭
3.特地初始化时关闭
Eager Loading
又称为 预加载 ,贪婪加载
作用:
- 一次性的把相关的数据全部查询出来
- 多用于循环遍历中
- 若是有多个循环需要加载数据,最好是一次性把数据加载书来,然后再使用
示例代码
1.简单做法:取消延迟加载,然后使用include进行贪婪加载
context.Configuration.LazyLoadingEnabled = false;
var query = from
clazz in
context.Clazz.Include("Students")
2.代码二:
using (var dbcontext= new ModelFirstDemoEntities())
{
#region 贪婪加载: 一次查询加载全部数据
var q = from t in dbcontext.Customer.Include("Order")
select t;
foreach (var cus in q)
{
Console.WriteLine("Teacher : {0}", cus.Id);
Console.WriteLine("Respective Courses...");
foreach (var order in cus.Order)
{
Console.WriteLine("Course name : {0}", order.OrderContent);
}
Console.WriteLine();
}
#endregion
}
3.代码三:
var orders = from o in
context.Orders.Include("OrderDetails").Include("Businesses")
where o.CustomerName == "Mac"
select o;
.Include(Entity)方法允许级联使用,你可以预先加载具有多层级结构的数据
Explicit Loading
显式加载的条件
1.不设置导航属性为virtual
2.对导航属性使用Reference(单个对象).Load()或Collection(对象集).Load();
- 如果实体具有指向另一个实体的导航属性,则应使用 Reference 方法。
- 如果实体具有指向其他实体的集合的导航属性,则应使用Collection方法
示例代码:
1.代码一:
using (var context = new BloggingContext())
{
var post = context.Posts.Find(2);
//加载与给定Post相关的Blog(单个实体)
context.Entry(post).Reference(p => p.Blog).Load();
// 加载与给定Post相关的Blog(单个实体)--- 使用字符串相关联
context.Entry(post).Reference("Blog").Load();
var blog = context.Blogs.Find(1);
// 加载与给定Post相关的Blog集合
context.Entry(blog).Collection(p => p.Posts).Load();
// 加载与给定Post相关的Blog集合
// 使用字符串相关联(字符串的复数)
context.Entry(blog).Collection("Posts").Load();
}
2.代码二:
using (var dbcontext= new ModelFirstDemoEntities())
{
dbcontext.Configuration.LazyLoadingEnabled = false;
#region 显式加载:查询部分列数据,前提关闭 懒加载
//查询表中部分列的数据
var items = from c in dbcontext.Customer
select c;
foreach (var item in items)
{
//条件判断,只加载满足条件的数据,减少访问数据库的次数
if (item.Id < 5)
{
dbcontext.Entry(item).Collection(c => c.Order).Load();
Console.WriteLine(item.CusName);
}
foreach (var order in item.Order)
{
Console.WriteLine("Course name : {0}", order.OrderContent);
}
}
#endregion
}
三者比较
延迟加载:
- 非常宽容,因为只在需要的时候加载数据,不需要预先计划
- 可能会因为数据访问的延迟而降低性能,考虑到每访问父实体的子实体时,就需要访问数据库。
贪婪加载
- 减少数据访问的延迟,在一次数据库的访问中返回所有的数据
- 减少与数据库的交互次数
显式加载
*加载关联数据上和延迟加载相似(主数据加载完后,再加载关联的数据)
- 不会自动发生,需要手动调用方法加载数据
- 但是可以通过条件筛选数据
- 显示加载允许知道查询什么时候发送到数据库。延迟加载会潜在的产生很多查询,而显示加载何时何地运行查询都是非常明显的2
EF的三种数据加载方式
EntityFramework循循渐进
【查询】—Entity Framework实例详解