Entity Framework加載控制Loading Entities
Entity Framework允許控制對象之間的關(guān)系,在使用EF的過程中,很多時候我們會進行查詢的操作,當(dāng)我們進行查詢的時候,哪些數(shù)據(jù)會被加載到內(nèi)存中呢?所有的數(shù)據(jù)都需要嗎?在一些場合可能有意義,例如:當(dāng)查詢的實體僅僅擁有一個相關(guān)的子實體時可以加載所有的數(shù)據(jù)到內(nèi)存中。但是,在多數(shù)情況下,你可能并不需要加載全部的數(shù)據(jù), 而是只要加載一部分的數(shù)據(jù)即可。
默認情況下,EF僅僅加載查詢中涉及到的實體,但是它支持兩種特性來幫助你控制加載:
- 1、貪婪加載
- 2、延遲加載
下面以客戶類型、客戶和客戶郵件三個實體之間的關(guān)系來講解兩種加載方式。

從上圖可以看出三個實體類之間的關(guān)系:
客戶類型和客戶是一對多的關(guān)系:一個客戶類型可以有多個客戶。
客戶和客戶郵件是一對一的關(guān)系:一個客戶只有一個郵箱地址。(假設(shè)只有一個郵箱地址)
一、延遲加載(Lazy Loading)
延遲加載:即在需要或者使用的時候才會加載數(shù)據(jù)。默認情況下,EF使用延遲加載的方式來加載數(shù)據(jù)。延遲加載是這樣一種過程:直到LINQ查詢的結(jié)果被枚舉時,該查詢涉及到的相關(guān)實體才會從數(shù)據(jù)庫加載。如果加載的實體包含了其他實體的導(dǎo)航屬性,那么直到用戶訪問該導(dǎo)航屬性時,這些相關(guān)的實體才會被加載。
使用延遲加載必須滿足兩個條件:
1、實體類是由Public修飾符修飾的,不能是封閉類。
2、導(dǎo)航屬性標記為Virtual。
1、定義實體類
CustomerType實體類定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LazyLoding.Model
{
public class CustomerType
{
public int CustomerTypeId { get; set; }
public string Description { get; set; }
// 導(dǎo)航屬性使用virtual關(guān)鍵字修飾,用于延遲加載
public virtual ICollection<Customer> Customers { get; set; }
}
}Customer實體類定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LazyLoding.Model
{
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
// 導(dǎo)航屬性使用virtual關(guān)鍵字修飾,用于延遲加載
public virtual CustomerType CustomerType { get; set; }
// 導(dǎo)航屬性使用virtual關(guān)鍵字修飾,用于延遲加載
public virtual CustomerEmail CustomerEmail { get; set; }
}
}CustomerEmail實體類定義如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LazyLoding.Model
{
public class CustomerEmail
{
public int CustomerEmailId { get; set; }
public string Email { get; set; }
// 導(dǎo)航屬性使用virtual關(guān)鍵字修飾,用于延遲加載
public virtual Customer Customer { get; set; }
}
}2、定義數(shù)據(jù)上下文類,并配置實體關(guān)系
using LazyLoding.Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LazyLoding.EF
{
public class Context :DbContext
{
public Context()
: base("name=AppConnection")
{
}
#region 將領(lǐng)域?qū)嶓w添加到DbSet中
public DbSet<CustomerType> CustomerTypes { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<CustomerEmail> CustomerEmails { get; set; }
#endregion
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// 設(shè)置表名和主鍵
modelBuilder.Entity<CustomerType>().ToTable("CustomerType").HasKey(p => p.CustomerTypeId);
modelBuilder.Entity<Customer>().ToTable("Customer").HasKey(p => p.CustomerId);
modelBuilder.Entity<CustomerEmail>().ToTable("CustomerEmail").HasKey(p => p.CustomerEmailId);
// 設(shè)置實體關(guān)系
/*
配置一對多關(guān)系
HasMany:表示一個CustomerType里面包含多個Customers
WithRequired:表示必選,CustomerType不能為空
MapKey:定義實體之間的外鍵
*/
modelBuilder.Entity<CustomerType>().HasMany(p => p.Customers).WithRequired(t => t.CustomerType)
.Map(m =>
{
m.MapKey("CustomerTypeId");
});
/*
配置一對一的關(guān)系
HasRequired:表示前者必選包含后者,前者可以獨立存在,后者不可獨立存在
WithRequiredPrincipal:指明實體的主要 這里表示指定Customer表是主表可以獨立存在
MapKey:定義實體之間的外鍵
*/
modelBuilder.Entity<Customer>().HasRequired(p => p.CustomerEmail).WithRequiredPrincipal(t => t.Customer)
.Map(m =>
{
m.MapKey("CustomerId");
});
base.OnModelCreating(modelBuilder);
}
}
}3、使用數(shù)據(jù)遷移生成數(shù)據(jù)庫,并重寫Configuration類的Seed()方法填充種子數(shù)據(jù)
Configuration類定義如下:
namespace LazyLoding.Migrations
{
using LazyLoding.Model;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<LazyLoding.EF.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(LazyLoding.EF.Context context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data.
// 初始化種子數(shù)據(jù)
context.CustomerTypes.AddOrUpdate(
new CustomerType()
{
Description = "零售",
Customers = new List<Customer>()
{
new Customer(){Name="小喬", CustomerEmail=new CustomerEmail(){ Email="qiao@qq.com"}},
new Customer(){Name="周瑜",CustomerEmail=new CustomerEmail(){Email="yu@126.com"}}
}
},
new CustomerType()
{
Description = "電商",
Customers = new List<Customer>()
{
new Customer(){Name="張飛", CustomerEmail=new CustomerEmail(){Email="zf@qq.com"}},
new Customer(){Name="劉備",CustomerEmail=new CustomerEmail(){Email="lb@163.com"}}
}
}
);
}
}
}4、查看生成的數(shù)據(jù)庫

5、查看Main方法,并打開SQL Server Profiler監(jiān)視器監(jiān)視數(shù)據(jù)庫
// 還沒有查詢數(shù)據(jù)庫 var customerType = dbContext.CustomerTypes;

繼續(xù)執(zhí)行

查看監(jiān)視器:

發(fā)現(xiàn)這時候產(chǎn)生了查詢的SQL語句。
這就是EF的延遲加載技術(shù),只有在數(shù)據(jù)真正用到的時候才會去數(shù)據(jù)庫中查詢。
使用Code First時,延遲加載依賴于導(dǎo)航屬性的本質(zhì)。如果導(dǎo)航屬性是virtual修飾的,那么延遲加載就開啟了,如果要關(guān)閉延遲加載,不要給導(dǎo)航屬性加virtual關(guān)鍵字就可以了。
注意:如果想要為所有的實體關(guān)閉延遲加載,那么可以在Context的構(gòu)造函數(shù)中配置關(guān)閉屬性即可,代碼如下:
public Context() : base("name=AppConnection")
{
// 配置關(guān)閉延遲加載
this.Configuration.LazyLoadingEnabled = false;
}二、貪婪加載(Eager Load)
貪婪加載:顧名思義就是一次性把所有數(shù)據(jù)都加載出來。貪婪加載是這樣一種過程:當(dāng)我們要加載查詢中的主要實體時,同時也加載與之相關(guān)的所有實體。要實現(xiàn)貪婪加載,我們要使用Include()方法。
下面我們看一下如何在加載Customer數(shù)據(jù)的時候,同時也加載所有的CustomerType數(shù)據(jù)(操作此功能時暫時先關(guān)閉延遲加載以免影響)。
//貪婪加載,以下兩種方式都可以
// 在使用Lambda表達式指明要加載的導(dǎo)航實體時,要引用命名空間:System.Data.Entity
var customers = dbContext.Customers.Include(p => p.CustomerType).Include(p => p.CustomerEmail).ToList();
//方式2
var query = dbContext.Customers.Include("CustomerType").Include("CustomerEmails");總結(jié):
貪婪加載:
- 1、減少數(shù)據(jù)訪問的延遲,在一次數(shù)據(jù)庫的訪問中返回所有的數(shù)據(jù)。
- 2、一次性加載所有的數(shù)據(jù)到內(nèi)存中,可能導(dǎo)致部分數(shù)據(jù)實際用不到,從而導(dǎo)致讀取數(shù)據(jù)的速度變慢,效率變低。
延遲加載:
- 1、只在需要讀取關(guān)聯(lián)數(shù)據(jù)的時候才進行加載。每一條數(shù)據(jù)都會訪問一次數(shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫的壓力加大。
- 2、可能因為數(shù)據(jù)訪問的延遲而降低性能,因為循環(huán)中,每一條數(shù)據(jù)都會訪問一次數(shù)據(jù)庫,導(dǎo)致數(shù)據(jù)庫的壓力增大。
如何選擇使用哪種查詢機制:
- 1、如果是在foreach循環(huán)中加載數(shù)據(jù),那么使用延遲加載會比較好,因為不需要一次性將所有數(shù)據(jù)都讀取出來,這樣雖然可能會造成多次查詢數(shù)據(jù)庫,但基本上在可以接受的范圍之內(nèi)。
- 2、如果在開發(fā)時就可以預(yù)見需要一次性加載所有的數(shù)據(jù),包含關(guān)聯(lián)表的所有數(shù)據(jù),那么使用貪婪加載是比較好的選擇,但是此種方式會導(dǎo)致效率問題,尤其是在數(shù)據(jù)量大的情況下。
代碼下載地址:點此下載
到此這篇關(guān)于Entity Framework加載控制Loading Entities的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Entity Framework使用LINQ操作實體
- Entity?Framework使用Code?First的實體繼承模式
- Entity Framework使用Code First模式管理數(shù)據(jù)庫
- Entity Framework表拆分為多個實體
- Entity?Framework管理一對二實體關(guān)系
- Entity?Framework管理一對一實體關(guān)系
- Entity?Framework實體拆分多個表
- Entity?Framework使用Fluent?API配置案例
- Entity?Framework實現(xiàn)數(shù)據(jù)遷移
- Entity?Framework使用配置伙伴創(chuàng)建數(shù)據(jù)庫
相關(guān)文章
asp.net core 集成swagger ui的原理解析
本文主要講解了如何對API進行分組,這里僅僅是舉了一個按照API功能進行分組的例子,其實在實際開發(fā)中,要按照何種方式分組,可以按照需求靈活定義,比如按照API版本進行分組2021-10-10
ASP.NET?Core?6.0?添加?JWT?認證和授權(quán)功能
這篇文章主要介紹了ASP.NET?Core?6.0?添加?JWT?認證和授權(quán),本文將分別介紹?Authentication(認證)?和?Authorization(授權(quán)),通過實例代碼分別介紹了這兩個功能,需要的朋友可以參考下2022-04-04
理解ASP.NET?Core?錯誤處理機制(Handle?Errors)
這篇文章主要介紹了理解ASP.NET?Core?錯誤處理(Handle?Errors)?,在這里需要注意的是,與“異常處理”有關(guān)的中間件,一定要盡早添加,這樣,它可以最大限度的捕獲后續(xù)中間件拋出的未處理異常。感興趣的朋友跟隨小編一起看看吧2021-11-11
Asp.Net在線預(yù)覽Word文檔的解決方案與思路詳解
這篇文章主要介紹了Asp.Net在線預(yù)覽Word文檔的解決方案與思路,大致思路是先將Word文檔轉(zhuǎn)換Html,再預(yù)覽Html,需要注意電腦安裝Office,需要的朋友可以參考下2022-04-04
ASP.NET GridView的Bootstrap分頁樣式
這篇文章主要為大家詳細介紹了ASP.NET GridView的Bootstrap分頁樣式,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11
一步一步學(xué)asp.net Ajax登錄設(shè)計實現(xiàn)解析
做一個登錄,擁有自動記住賬號和密碼的功能,要保證安全性,ajax,無刷新,良好的用戶體驗.(母板頁)2012-05-05

