Entity Framework中IQueryable, IEnumerable, IList的區別

轉自:http://www.cnblogs.com/hiteddy/archive/2011/10/01/Difference_among_IQueryable_IEnumeralb_IList_in_Entity_Framework.html


此文章講解了Entity Framework中IQueryable, IEnumerable, IList的區別,分享一下。

使用工具追蹤EF生成的SQL

使用Entity Framework等ORM框架的時候,SQL對於使用者來說是透明的,往往很多人也不關心ORM所生成的SQL,然而系統出現性能問題的時候就必須關注生成的SQL以發現問題所在。

使用過Toplink的朋友知道很只要設置日誌打印級別=FINE就可以配置使之生成的SQL在服務器中打印出來,Entiry Framework沒有那麼幸運,在以前要檢測生成SQL的唯一方法是SQL Server Profiler,但使用起來並不方便,結果也不能自動保存到文件中。

Tracing and Caching Provider Wrappers for Entity Framework是Entity Framework Team新推出的開源SQL追蹤和二級緩存的解決方案。原理是在負責執行具體SQL語句的data provider(SqlClient或者其他Client)之上插入了一層WrappingProvider,用於監控DbCommand.ExecuteReader(), ExecuteScalar() and ExecuteNonQuery(),將Sql命令輸出到指定介質或者將查詢結果緩存起來以重用。

使用方法很簡單,下載源代碼編譯後將dll添加到項目中,新加一個類WrappedNorthWindEntities繼承原有的Entities即可,詳見源代碼中的示例。

測試IQueryable, IEnumerable, IList的區別

下面我們使用EF Wrapper來監測Entify Framework中IQueryable, IEnumerable和IList所生成的SQL。

複製代碼

   TestIQueryable()
{
     (var ctx =  WrappedNorthWindEntities())
    {
        IQueryable<Product> expression = ctx.Products.Take();
        IQueryable<Product> products = expression.Take();   
        Console.WriteLine(products.Count());          
        Console.WriteLine(products.Count());          
         (Product p  products)             
        {
            Console.WriteLine(p.ProductName);
        }
         (Product p  products)              
        {
            Console.WriteLine(p.ProductName);
        }
    }
}

複製代碼

 

複製代碼

   TestIEnumerable()
{
     (var ctx =  WrappedNorthWindEntities())
    {
        IEnumerable<Product> expression = ctx.Products.Take().;
        IEnumerable<Product> products = expression.Take();  
        Console.WriteLine(products.Count());          
        Console.WriteLine(products.Count());          
         (Product p  products)             
        {
            Console.WriteLine(p.ProductName);
        }
         (Product p  products)              
        {
            Console.WriteLine(p.ProductName);
        }
    }
}

複製代碼

 

複製代碼

   TestIList()
{
     (var ctx =  WrappedNorthWindEntities())
    {
        var expression = ctx.Products.Take();
        IList<Product> products = expression.Take().; 

        Console.WriteLine(products.Count());            
        Console.WriteLine(products.Count());            
         (Product p  products)               
        {
            Console.WriteLine(p.ProductName);
        }
         (Product p  products)                
        {
            Console.WriteLine(p.ProductName);
        }
    }
}

複製代碼

測試結果

  1. IQueryable和IEnumerable都是延時執行(Deferred Execution)的,而IList是即時執行(Eager Execution)

  2. IQueryable和IEnumerable在每次執行時都必須連接數據庫讀取,而IList讀取一次後,以後各次都不需連接數據庫。前兩者很容易造成重複讀取,性能低下,並且可能引發數據不一致性

  3. IQueryable和IEnumerable的區別:IEnumberalb使用的是LINQ to Object方式,它會將AsEnumerable()時對應的所有記錄都先加載到內存,然後在此基礎上再執行後來的Query。所以上述TestIEnumerable例子中執行的SQL是"select top(5) ...",然後在內存中選擇前兩條記錄返回。

以下是一個IQueryable引發數據不一致性的例子:記錄總數和記錄詳情兩者本應一致,但由於IQueryable前後兩次讀取數據庫,結果是現實有10條記錄,卻輸出11條詳情。

複製代碼

    IQueryable<Product> products = ctx.Products.All();
 count = products.Count();
    Console.WriteLine(+count);
        
   //此時另一進程添加一個產品進數據庫
 (Product p  products)       
    {
    Console.WriteLine(p.ProductName);    
    }

複製代碼

結論

基於性能和數據一致性這兩點,我們使用IQueryable時必須謹慎,而在大多數情況下我們應使用IList

  • 當你打算馬上使用查詢後的結果(比如循環作邏輯處理或者填充到一個table/grid中),並且你不介意該查詢會即時執行,使用ToList()

  • 當你希望查詢後的結果可以供調用者(Consummer)作後續查詢(比如這是一個"GetAll"的方法),或者你希望該查詢延時執行,使用AsQueryable()


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章