Entity framework 中Where、First、Count等查詢函數使用時要注意


在.Net開發中,Entity framework是微軟ORM架構的最佳官方工具。我們可以使用Lambda表達式在Entity framework中DbSet<T>類上直接做查詢(比如使用DbSet<T>類的Where、First、Count等查詢函數)返回數據庫結果實體。

 

不知道大家有沒有注意到DbSet<T>類上的很多查詢函數都有兩種類型的重載,就拿Where這個查詢函數舉例:

 

一種是傳入Func<Tsource, bool>委托作為參數


 

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

這種Where函數的重載返回的是IEnumerable<TSource>集合類型。

調用的示例如下面代碼所示:

//CustomerDbContext為Entity framework中的DbContext
using (var customerDbContext = new CustomerDbContext())
{
    //顯示Entity framework底層調用的Sql語句到Visual Studio的輸出窗口
    customerDbContext.Database.Log = (message) => {
        Debugger.Log(0, "Sql", message);
    };

    Func<Mid_TriaBalance, bool> func = r => r.ID == 1;//使用lambda表達式查詢ID為1的數據庫數據
    var triaBalance = customerDbContext.Mid_TriaBalance.Where(func).First();
}

我們在上面的代碼中使用了DbContext的Log委托,顯示Entity framework生成的底層Sql語句到Visual Studio,運行該代碼,我們來看看生成的Sql語句是什么,結果如下截圖:

結果讓人大跌眼鏡,我們發現實際上Entity framework生成的Sql沒有包含任何Where條件,是將整張Mid_TriaBalance表的數據都返回到C#代碼后,再過濾出ID等於1的這一行數據。這種方式當Mid_TriaBalance表的數據比較少的時候還好,但是一旦Mid_TriaBalance表的數據很大比如100萬行,那么我們為了查詢ID為1的這一行數據,就需要將Mid_TriaBalance表中的100萬行數據從數據庫中取出先放到內存中,再去篩選ID為1的這一行數據,效率低下可想而知,還有可能造成服務器內存溢出。

 


一種是傳入Expression<Func<TSource, bool>>類型作為參數:


 

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

這種Where函數的重載返回的是IQueryable<TSource>集合類型。

調用的示例如下面代碼所示:

//CustomerDbContext為Entity framework中的DbContext
using (var customerDbContext = new CustomerDbContext())
{
    //顯示Entity framework底層調用的Sql語句到Visual Studio的輸出窗口
    customerDbContext.Database.Log = (message) => {
        Debugger.Log(0, "Sql", message);
    };

    Expression<Func<Mid_TriaBalance, bool>> exp = r => r.ID == 1;//使用lambda表達式查詢ID為1的數據庫數據
    var triaBalance = customerDbContext.Mid_TriaBalance.Where(exp).First();
}

同樣我們在上面的代碼中使用了DbContext的Log委托,顯示Entity framework生成的底層Sql語句到Visual Studio,運行該代碼,我們來看看生成的Sql語句是什么,結果如下截圖:

這次我們看到Where函數使用Expression<Func<TSource, bool>>類型傳入Lambda表達式后,Entity framework在底層生成了我們期望的Sql語句,包含了Where條件去限制數據庫只查詢ID為1的數據。很明顯這種方式只會從數據庫返回ID為1的一行數據到C#代碼,效率和性能明顯優於傳入Func<Tsource, bool>委托的重載方式。

 

 

總結 

我們可以看到這兩種調用方式乍看之下覺得差別不大,最終都是返回Mid_TriaBalance表中ID為1的這一行數據。但是后台生成的Sql語句卻有天壤之別,查詢性能也有天壤之別。應該在Entity framework的Where、First、Count等查詢函數中,避免使用傳入Func<Tsource, bool>委托這種重載,因為這種重載在后台生的Sql語句中是不帶任何Where限制條件的,是將整張表的數據先從數據庫查出來后,放到C#代碼內存中再做過濾,非常低效。

 

此外Expression<Func<TSource, bool>>類的構造函數無法直接調用,我們是無法去直接new一個Expression<Func<TSource, bool>>對象的,只有通過將lambda表達式直接賦值給Expression<Func<TSource, bool>>類型做隱式轉換,C#會自動生一個Expression<Func<TSource, bool>>對象如下面代碼所示:

Expression<Func<Mid_TriaBalance, bool>> exp = r => r.ID == 1;//使用lambda表達式直接給Expression<Func<TSource, bool>>類型賦值,C#會幫助我們生成一個Expression<Func<TSource, bool>>對象

另外注意,不能將Func<TSource, bool>委托直接賦給Expression<Func<TSource, bool>>類型,這兩種類型沒法做類型轉換,如下圖所示,代碼編譯會報錯:

 


免責聲明!

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



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