>Andy , 2023/05/11
# IEnumerable與IQueryable
## 概述
兩者都是用來執行LINQ的Interface,主要的差別在於查詢的方式與執行的時機。
先定義一個DbContext,以便後續說明,並假設Book資料表中有10筆資料。
```csharp
var dbContext = new DbContext();
```
## IEnumerable
從資料庫中撈取資料,存至記憶體中,並在記憶體中篩選。
```csharp
//此時的data,已經將資料載入記憶體當中,記憶體有全部10筆資料。
IEnumerable<Book> Edata = dbContext.Book;
//即使在定義變數時加入了where條件式,記憶體中同樣是載入全部10筆資料
IEnumerable<Book> filterEdata = dbContext.Book.Where(x => x.Id ==1);
```

## IQueryable
在IDE中寫好的IQueryable只是"查詢狀態",此時還沒執行資料庫的查詢,因此不會有資料載入記憶體的行為。若指派某些會得到"明確結果"的function,如Count()、ToList()等,此時才會執行SQL查詢指令,取得查詢結果。
以下程式定義的變數都會得到IQueryable物件,實際上不會去執行SQL查詢,而是將查詢轉換成Expresion,並將所有查詢條件存於Provider中。
```csharp
//此時的data是"查詢狀態",資料還未載入記憶體中。
IQueryable<Book> Qdata = dbContext.Book;
//在定義變數時加入條件式,也不會將"查詢狀態"載入記憶體中。
IQueryable<Book> filterQdata = dbContext.Book.Where(x => x.Id == 1);
```
以下程式會真的執行SQL查詢,IQueryable介面會將Expression傳遞給Provider,由Provider轉譯成T-SQL後,從DB中取得資料,得到"明確結果"
```
//下面兩行程式才會執行SQL指令,並將查詢的資料載入記憶體中。
var book = filterData.ToList();
var book = filterData.Count();
```

如果在宣告變數時不指定資料型態,會預設指定IQueryable物件。
```csharp
//data的資料型態是IQueryable物件
var data = dbContext.Book;
```
## 注意事項
了解原理之後,才能避免浪費效能的寫法。
由於IEnymerable與IQueryable都有延遲載入的機制,因此使用之前應該先將查詢結果保存,以避免重新查詢。
以下程式是不良示範,每跑一次迴圈都會訪問一次資料庫,如果查詢結果資料量大,就會造成效能降低的負面影響。
```csharp
IEnumerable<Book> query = dbContext.Book;
foreach(var item in query)
{
var book name = query.Where(x => x.Price < 300);
//do
}
```
存成List就可以避免這個問題。
```csharp
List<Book> query = dbContext.Book.ToList();
foreach(var item in query)
{
var book name = query.Where(x => x.Price < 300);
//do
}
```
###### tags: `back-end`