zoukankan      html  css  js  c++  java
  • MVC系列学习(三)-EF的延迟加载

    1.什么叫延迟加载

    字面上可以理解为,一个动作本该立即执行的动作,没有立即执行

    2.从代码上理解

    static void Main(string[] args)
    {
        //执行该语句的时候,查看sql监视器,发现并没有生成sql语句
        IEnumerable<Student> stu = dbContext.Students.Where(s => s.Id == 1).Select(s => s);
        //只有当  使用的时候  ,才生成sql语句
        Student student = stu.FirstOrDefault();
    }

    只有对象被使用了,才生成sql语句

    3.寻找原因,什么原因导致延迟加载

    先理解两个Where()方法:

    a.集合的Where()

    List<string> listStr = new List<string>()
    {
        "A",
        "BB",
        "CCC"
    };
    string  bb = listStr.Where(s => s.Length == 2).Select(s => s).FirstOrDefault();

    转到Where的定义,发现 集合 中的Where方法 实际上是IEnumerable的扩展方法,该接口继承与IEnumerable

    public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
    public interface IEnumerable<out T> : IEnumerable

    b.DbSet中的Where方法

    dbContext.Students是DbSet<Student>类型,所以此处说 DbSet的中Where方法

    var stu = dbContext.Students.Where(s => s.Id == 1);
    public partial class SchoolEntities : DbContext
    {
        public virtual DbSet<Student> Students { get; set; }
    }

    image

    dbContext.Students是DbSet<T>类型的,转到DbSet<T>定义看看

    此处的Where()是IQueryable的扩展方法,继承与IQueryable

    public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
    public interface IQueryable<out T> : IEnumerable<T>, IQueryable, IEnumerable
    public interface IQueryable : IEnumerable

    c.IEnumerable与IQueryable的区别

    var s1 = dbContext.Students.Where(s => s.Id == 1);
    var s2 = s1.Where(s => s.Age > 0);
    var s3 = s2.Select(s => s).FirstOrDefault();

    用sql监视器查看,发现总共就执行了一次sql查询

    得出结论:

    1.实现EF延迟加载 的 实际上是IQueryable上的扩展方法,更具体的话是DbQuery类型来实现的
    2.IEnumerable<T>将命令直接执行,第一次执行后的结果,保存到内存,不会拼接命令树
    3.IQueryable<T>将语句拼接成一个命令树,当用到的时候,再执行
    4.两种操作都只访问了一次数据库

    4.为什么要有延迟加载

    a.无法确定 本次查询条件 是否 已经添加结束

    DbQuery<Student> s1 = dbContext.Students.Where(s => s.Id == 1).Where(s => s.Age > 0) as DbQuery<Student>;

    每次添加 查询条件的时候,都只是返回 包含所有添加条件的 DbQuery对象,只有最后使用的时候,才根据条件生成相应的sql语句

    b.对于外键实体,按需加载

    本次需要用到的两张 具有 主外键关系的两张表如下

    image

    image

    var tea = dbContext.Teachers.Where(t => t.tId == 1);
    //生成sql语句,如图 代码一
    Teacher teacher = tea.FirstOrDefault();
    //生成sql语句,如图 代码二
    string className = teacher.TeachClass.cName;

    代码一,如下图:

    SELECT TOP (1) 
        [Extent1].[tId] AS [tId], 
        [Extent1].[tName] AS [tName], 
        [Extent1].[tAge] AS [tAge], 
        [Extent1].[tClass] AS [tClass]
        FROM [dbo].[Teacher] AS [Extent1]
        WHERE 1 = [Extent1].[tId]

    代码二,如下图:

    exec sp_executesql N'SELECT 
        [Extent1].[cId] AS [cId], 
        [Extent1].[cName] AS [cName]
        FROM [dbo].[TeachClass] AS [Extent1]
        WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

    观察,得出结论:

    1.按需加载:在第一次执行的时候,因为没有用到外键属性,所以生成sql语句的时候,不会去去查询 TeachClass表
    2.当EF需要用到外键属性的时候,才会去加载 相应的表

    c.按需加载的缺点:

    实例代码如下:

    DbQuery<Teacher> teachers = dbContext.Teachers;
    StringBuilder sbTeacher=new StringBuilder(100);
    foreach (Teacher tea in teachers)
    {
        //每次调用 外键表Teachers上 的 外键实体时,都会去查询数据库
        //EF有个优化,相同的外键实体只查一次,即TeachClass相同只查一次
        sbTeacher.Append(tea.TeachClass.cName);
    }

    生成的SQL脚本如下:

    exec sp_executesql N'SELECT 
        [Extent1].[cId] AS [cId], 
        [Extent1].[cName] AS [cName]
        FROM [dbo].[TeachClass] AS [Extent1]
        WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

    第二次,和第三次,因为TeachClass的值相同,则只查询了一次

    exec sp_executesql N'SELECT 
        [Extent1].[cId] AS [cId], 
        [Extent1].[cName] AS [cName]
        FROM [dbo].[TeachClass] AS [Extent1]
        WHERE [Extent1].[cId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2

    5.连接查询

    既然EF是只有用到外键实体的时候,才加载相应表,那么如果我们要连接两张表要怎么做

    a.通过Include方法

    DbQuery<Teacher> teachers = dbContext.Teachers.Include("TeachClass");
    StringBuilder sbTeacher=new StringBuilder(100);
    foreach (Teacher tea in teachers)
    {
        //只有第一次 查询的使用,将数据查询 并保存到内存中,
        //接下来的操作只是在内存中读取,并没有读取数据库
        sbTeacher.Append(tea.TeachClass.cName);
    }

    查看sql语句,发现 EF为我们生成 left outer  join ,连接了两张表

    SELECT 
        [Extent1].[tId] AS [tId], 
        [Extent1].[tName] AS [tName], 
        [Extent1].[tAge] AS [tAge], 
        [Extent1].[tClass] AS [tClass], 
        [Extent2].[cId] AS [cId], 
        [Extent2].[cName] AS [cName]
        FROM  [dbo].[Teacher] AS [Extent1]
        LEFT OUTER JOIN [dbo].[TeachClass] AS [Extent2] ON [Extent1].[tClass] = [Extent2].[cId]

    b.生成 join 的另一种方式

    var teachers = dbContext.Teachers.Select(t => new {tName = t.tName, ClassName = t.TeachClass.cName}).ToList();

    生成的sql语句如下

    SELECT 
        1 AS [C1], 
        [Extent1].[tName] AS [tName], 
        [Extent2].[cName] AS [cName]
        FROM  [dbo].[Teacher] AS [Extent1]
        LEFT OUTER JOIN [dbo].[TeachClass] AS [Extent2] ON [Extent1].[tClass] = [Extent2].[cId]
    更多精彩内容请看:http://www.cnblogs.com/2star
  • 相关阅读:
    手把手教你利用create-nuxt-app脚手架创建NuxtJS应用
    初识NuxtJS
    webpack打包Vue应用程序流程
    用选择器代替表格列的筛选功能
    Element-UI
    Spectral Bounds for Sparse PCA: Exact and Greedy Algorithms[贪婪算法选特征]
    Sparse Principal Component Analysis via Rotation and Truncation
    Generalized Power Method for Sparse Principal Component Analysis
    Sparse Principal Component Analysis via Regularized Low Rank Matrix Approximation(Adjusted Variance)
    Truncated Power Method for Sparse Eigenvalue Problems
  • 原文地址:https://www.cnblogs.com/kimisme/p/4448597.html
Copyright © 2011-2022 走看看