程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> MVC系列學習(三)-EF的延遲加載,mvc-ef

MVC系列學習(三)-EF的延遲加載,mvc-ef

編輯:C#入門知識

MVC系列學習(三)-EF的延遲加載,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方法

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

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.對於外鍵實體,按需加載

本次需要用到的兩張 具有 主外鍵關系的兩張表如下

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]

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved