[LINQ] LINQ to Entity Framework,Where 的陷阱

  • 489
  • 0
  • 2017-11-25

上了黃忠成老師的課,才知道平時用得很開心自在的 Where 竟然藏有陷阱!

 

下列三段程式,何者結果可能與其他兩者不同?(差異只在 items 的生成方式)

1. Lambda Expression

static void QueryData()
{
    var context = new Model1();
    var items = context.EMPLOYEE.Where(s => s.NAME == "code6421");
    Console.WriteLine($"COUNT = {items.Count()}");
}

2. Delegate

static bool FindName(EMPLOYEE employee)
{
    return employee.NAME == "code6421";
}

static void QueryData()
{
    var context = new Model1();
    var items = context.EMPLOYEE.Where(FindName);
    Console.WriteLine($"COUNT = {items.Count()}");
}

3. LINQ Expression

private static void QueryData()
{
    var context = new Model1();
    var items = from s1 in context.EMPLOYEE
                where s1.NAME == "code6421"
                select s1;

    Console.WriteLine($"COUNT = {items.Count()}");
}

 

 

想好了嗎...?

 

 

 

 

答案是 2.

3 跟 1 是相同的語意,而 1 的 Lambda Expression,是 IQueryable<T> 的 method,但 2 的 Delegate 其實是 IEnumerable<T> 的 method 啊!

所以語意不全然相同,在某些情況下,結果會不相同。

例如若該欄位字尾含有空白的狀況下,結果可能就會不一樣。

但結果不相同還不要緊,最可怕的就是結果相同,但運行經過是全然不同的兩回事,完全沒有發現問題。在程式碼中加上下列語句以便看見轉譯出來的 SQL,觀察運行經過。

context.Database.Log = (msg) => Console.WriteLine(msg);

方法一 Lambda Express 轉譯出來的 SQL 語法為:

SELECT
    [GroupBy1].[A1] AS [C1]
    FROM ( SELECT
        COUNT(1) AS [A1]
        FROM [dbo].[EMPLOYEE] AS [Extent1]
        WHERE N'code642125' = [Extent1].[NAME]
    )  AS [GroupBy1]

方法二 Delegate 轉譯出來的 SQL 語法為:

SELECT
    [Extent1].[ID] AS [ID],
    [Extent1].[NAME] AS [NAME],
    [Extent1].[Salary] AS [Salary]
    FROM [dbo].[EMPLOYEE] AS [Extent1]

看見了嗎?關鍵的 WHERE 不見了!!其實方法二的方式是把資料全部拉回來再進行 IEnumerable<T> 的 Where 處理,這效能可想而知。如果程式碼充斥著這玩意,那真的就可怕了~

不只 WHERE 呢,連 COUNT 也是拉回來處理的~!