ASP.NET EF 延遲加載,導航屬性延遲加載


EF(EntityFramework)原理:屬於ORM的一種實現

通過edmx文件來查看三部分:概念模型,數據模型,映射關系,上下文DbContext完成連接、狀態跟蹤管理,核心類是EntityClient完成映射

EF(EntityFramework)延遲加載:

>1:EF查詢默認會延遲加載

>2:EF對於集合類型的導航屬性會延遲加載

本質:IQueryable擁有3個成員,Expression,Type,Provider

IQueryable與IEnumberable對比區別:

IQueryable: 可以拼接一個完成的SQL語句,然后請求數據庫,拿到需要的數據

IEnumberable:直接把第一個命令請求數據庫,然后拿到數據,在內存當中對於后續條件進行篩選。

把IQueryable 轉換為IEnumberable :IQueryable.AsEnumberable();

EF非延遲加載:使用ToList()方法將結果立即拿到內存中(最好把命令全部拼接完之后使用ToList())

EF導航屬性的非延遲加載Include("") 可以使導航屬性非延遲加載

EF延遲加載 優點:用時才加載數據,保證數據的有效性

EF延遲加載 缺點:每次訪問都加載一次,加重了數據庫服務器的負擔

create database MyFirstEF
on primary
(
    name='MyFirstEF.mdf',
    --修改為自己電腦上SQL DB路徑
    filename='E:\ProgramMSSQLServerDB\MyFirstEF.mdf',
    size=5mb,
    maxsize=100mb,
    filegrowth=10%
)
log on
(
    name='MyFirstEF_log.ldf',
    --修改為自己電腦上SQL DB路徑
    filename='E:\ProgramMSSQLServerDB\MyFirstEF_log.ldf',
    size=2mb,
    maxsize=100mb,
    filegrowth=5mb
)
go

use MyFirstEF
go

create table CustomerInfo
(
    id int identity(1,1) primary key,
    customerName nvarchar(100) not null,
    customerDate datetime
)
go

create table OrderInfo
(
  id int identity(1,1) primary key,
  orderName nvarchar(100),
  customerId int
)
go

alter table OrderInfo
add constraint FK_OrderInfo_CustomerInfo foreign key(customerId) references CustomerInfo(id)
go

insert into CustomerInfo 
select 'aa',GETDATE() union all
select 'bb',GETDATE() union all
select 'cc',GETDATE() union all
select 'dd',GETDATE() 
go

insert into OrderInfo
select 'bike1',2 union all
select 'bike2',2 union all
select 'car1',3 union all
select 'car2',3 union all
select 'chezi1',4 union all
select 'chezi2',4 
go

select * from CustomerInfo
go

select * from OrderInfo
go
--create SQL

>1:EF查詢默認會延遲加載

DbContext context = new MyFirstEFEntities();
//1:EF默認延遲加載,執行完下面的語句,數據庫並沒有SQL查詢語句
var rows = context.Set<CustomerInfo>().Select(c => c);
//2:查詢一次數據庫
Console.WriteLine(rows.Count());
//3:第二次查詢數據庫
Console.WriteLine(rows.Count());

使用MS SQL Server Profiler(工具-->SQL Server Profiler),可以監測到上面代碼執行了兩次查詢數據庫操作

兩次查詢數據庫SQL:

SELECT 
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT 
        COUNT(1) AS [A1]
        FROM [dbo].[CustomerInfo] AS [Extent1]
    )  AS [GroupBy1]

>EF非延遲加載:ToList()方法

DbContext context = new MyFirstEFEntities();
//1:直接根據拼接SQL 查詢數據庫
var rows = context.Set<CustomerInfo>().Select(c => c).ToList();

//2:在內存中統計Count() 不會重新查詢數據庫
Console.WriteLine(rows.Count());
//3:在內存中統計Count() 不會重新查詢數據庫
Console.WriteLine(rows.Count());

使用MS SQL Server Profiler(工具-->SQL Server Profiler),可以監測到上面代碼在拼接SQL完成時,直接查詢數據庫

對應SQL:

SELECT 
    [Extent1].[id] AS [id], 
    [Extent1].[customerName] AS [customerName], 
    [Extent1].[customerDate] AS [customerDate]
    FROM [dbo].[CustomerInfo] AS [Extent1]

 >2:EF對於集合類型的導航屬性會延遲加載

DbContext context = new MyFirstEFEntities();
//1:EF默認延遲加載,執行完下面的語句,數據庫並沒有SQL查詢語句
var rows = context.Set<CustomerInfo>().Select(c => c);
//2:第一次查詢數據庫,查詢CustomerInfo表
foreach (var row in rows)
{
    //foreach時  這個會執行多次  每次@EntityKeyValue1 等於 迭代到這次的 OrderInfoId
    Console.WriteLine(row.OrderInfoes.Count);
}

使用MS SQL Server Profiler(工具-->SQL Server Profiler),可以監測到執行到foreach時,執行CustomerInfo表格數據的查詢(即為:EF查詢默認會延遲加載)

此時對應SQL為:

SELECT 
    [Extent1].[id] AS [id], 
    [Extent1].[customerName] AS [customerName], 
    [Extent1].[customerDate] AS [customerDate]
    FROM [dbo].[CustomerInfo] AS [Extent1]

foreach循環時  這個會執行多次  每次@EntityKeyValue1 等於 迭代到這次的 OrderInfoId,對應SQL為:

exec sp_executesql N'SELECT 
    [Extent1].[id] AS [id], 
    [Extent1].[orderName] AS [orderName], 
    [Extent1].[customerId] AS [customerId]
    FROM [dbo].[OrderInfo] AS [Extent1]
    WHERE [Extent1].[customerId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

用MS SQL Server Profiler(工具-->SQL Server Profiler),可以監測到執行到foreach時 執行了4次數據庫查詢(rows有4條CustomerInfo數據)

//也就是說 我們有多少條OrderInfo 就要執行多少次上面的查詢SQL   當然 這里使用的是exec sp_executesql   利用sp_executesql,能夠重用執行計划,這就大大提供了執行性能

 >EF導航屬性的非延遲加載: Include("")方法

DbContext context = new MyFirstEFEntities();
//1:EF默認延遲加載,執行完下面的語句,數據庫並沒有SQL查詢語句
var rows = context.Set<CustomerInfo>().Include(c=>c.OrderInfoes).Select(c => c);
//2:第一次查詢數據庫,查詢CustomerInfo表,以及CustomerInfo對應的所有OrderInfo數據
foreach (var row in rows)
{
    //foreach時,不會執行數據庫查詢操作
    Console.WriteLine(row.OrderInfoes.Count);
}

使用MS SQL Server Profiler(工具-->SQL Server Profiler),可以監測到執行foreach時,執行CustomerInfo表格數據的查詢(EF查詢默認會延遲加載),以及CustomerInfo對應的所有OrderInfo數據(EF導航屬性的非延遲加載)

對應SQL為:

SELECT 
    [Project1].[id] AS [id], 
    [Project1].[customerName] AS [customerName], 
    [Project1].[customerDate] AS [customerDate], 
    [Project1].[C1] AS [C1], 
    [Project1].[id1] AS [id1], 
    [Project1].[orderName] AS [orderName], 
    [Project1].[customerId] AS [customerId]
    FROM ( SELECT 
        [Extent1].[id] AS [id], 
        [Extent1].[customerName] AS [customerName], 
        [Extent1].[customerDate] AS [customerDate], 
        [Extent2].[id] AS [id1], 
        [Extent2].[orderName] AS [orderName], 
        [Extent2].[customerId] AS [customerId], 
        CASE WHEN ([Extent2].[id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM  [dbo].[CustomerInfo] AS [Extent1]
        LEFT OUTER JOIN [dbo].[OrderInfo] AS [Extent2] ON [Extent1].[id] = [Extent2].[customerId]
    )  AS [Project1]
    ORDER BY [Project1].[id] ASC, [Project1].[C1] ASC

關閉延遲加載的方式:

1.去掉屬性里的virtual

2.context.Configuration.LazyLoadingEnabled = false;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM