貪婪加載:顧名思議就是把所有要加載的東西一 次性讀取
1 using (var context = new MyDbContext()) 2 { 3 var orders = from o in context.Orders.Include("OrderDetails") select o; 4 }
當讀取訂單信息orders的時候,我們希望把訂單的詳細信息也讀取出來,那么這里我們使用Include關鍵字將關聯表也加載進 來。
延遲加載:即當我們需要用到的時候才進行加載(讀取)
當我們希望瀏覽某條訂單信息的時候,才顯示其對應的訂單詳細記錄時,我們希望使用延遲加載來實現,這樣不僅加快的了 讀取的效率,同時也避免加載不需要的數據。延遲加載通常用於foreach循環讀取數據時。
那么我們在定義Model的時候,需要在屬性前面添加virtual關鍵字。如下
1 public class Order 2 { 3 public int OrderID { get; set; } 4 public string OrderTitle { get; set; } 5 public string CustomerName { get; set; } 6 public DateTime TransactionDate { get; set; } 7 public virtual List<OrderDetail> OrderDetails { get; set; } 8 }
如果我們想要禁止使用延遲加載,那么最好的方法是在DbContext類的構造方法中聲明
1 public class MyDbContext:DbContext 2 { 3 public MyDbContext() 4 { 5 this.Configuration.LazyLoadingEnabled = false; 6 } 7 }
總結:
貪婪加載: 1、減少數據訪問的延遲,在一次數據庫的訪問中返回所有的數據。 2、一次性讀取所有相關的數據,可能導致部分數據實際無需用到,從而導致讀取數據的速度變慢,效率變低
延遲加載: 1、只在需要讀取關聯數據的時候才進行加載 2、可能因為數據訪問的延遲而降低性能,因為循環中,每一條數據都會訪問一次數據庫,導致數據庫的壓力加大
綜上所述,我們應該比較清楚時候應該使用哪種機制?我個人的建議是:
1、如果是在foreach循環中加載數據,那么使用延遲加載會比較好, 因為不需要一次性將所有數據讀取出來,這樣雖然有可能會造成n次數據庫的查詢,但 是基本上在可以接受的范圍內。
2、如果在開發時就可以預見需要一次性加載所有的數據,包含關聯表的所有數據, 那么使用使用貪婪加載是比較好的選擇,但是此種方式會導致效率問題,特別是數據量大的情況下。
兩張表:訂單表(Order_Info)和產品表(Order_Detail)
訂單表:包含2條訂單
產品表:4件產品,分別屬於上面兩個訂單
優化一
問題:查詢每件產品屬於哪個訂單時,需要連接幾次數據庫?
本應該查詢4次,EF做了優化后,查詢2次。
- public static void QueryUser()
- {
- IQueryable<Order_Detail>query = db.Order_Detail.Where(a => a.OrderID>0);
- foreach (Order_Detail detail inquery)
- {
- Console.WriteLine("產品" + detail.ProductName
- + ",所屬訂單" + detail.Order_Info.OrderID);
- }
- }
public static void QueryUser() { IQueryable<Order_Detail>query = db.Order_Detail.Where(a => a.OrderID>0); foreach (Order_Detail detail inquery) { Console.WriteLine("產品" + detail.ProductName + ",所屬訂單" + detail.Order_Info.OrderID); } }
為什么查詢了2次?
當他發現4條產品的訂單號有重復的時候,他就讀取他自己的緩存數據,就不讀取數據庫里面的數據里,這是EF做的一個小優化。
優化二 include進行inner join查詢
雖然EF為我們做了優化一,那當我們有1000個產品時,即使讀取又重復的產品,那也需要去讀1000次。之前我們只需要一個inner join就可以一次性讀取出來。
- //這里的include需要加載的文字,是從 Orderil_Detail的表結構里面訂單的屬性名字來復制的,注意是屬性名字,而不是屬性的類
- IQueryable<Order_Detail>query = db.Order_Detail.Include("Order_Info").Where(a =>a.OrderID>0);
- foreach (Order_Detail detail inquery)
- {
- Console.WriteLine("產品" + detail.ProductName
- + ",所屬訂單" + detail.Order_Info.OrderID);
- }
//這里的include需要加載的文字,是從 Orderil_Detail的表結構里面訂單的屬性名字來復制的,注意是屬性名字,而不是屬性的類 IQueryable<Order_Detail>query = db.Order_Detail.Include("Order_Info").Where(a =>a.OrderID>0); foreach (Order_Detail detail inquery) { Console.WriteLine("產品" + detail.ProductName + ",所屬訂單" + detail.Order_Info.OrderID); }
通過使用include,我們可以實現查詢一次數據庫即可,相當於之前的inner join。如果關聯多個表,可以使用多個include進行關聯。