上了黃忠成老師的課,才知道平時用得很開心自在的 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 也是拉回來處理的~!