C#之EF 使用篇之EF加载数据的方式

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.特地初始化时关闭
每次初始化entities时,设定关闭

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实例详解


  1. EF4 懒加载是关闭的,EF6懒加载是打开的​ ↩︎

  2. https://www.cnblogs.com/nianming/archive/2013/01/09/2846952.html ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值