[英]Why one query works and not the other?
我正在使用幫助器方法根據用戶訪問權限預過濾我的所有查詢。
假設方法簽名為:
public IQueryable<Client> GetAllClients()
使用LINQ時為什么這樣做:
IQueryable<Client> allItems = GetAllClients();
return (from item in allItems
where item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
select item).FirstOrDefault();
但不是這個:
return (from item in GetAllClients()
where item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
select item).FirstOrDefault();
我可以做第一個,但是離開LINQ幾年之后,理解這個問題的原因會很好。
不工作我的意思是選項2給出了這個例外:
EntityFramework.SqlServer.dll中發生了'System.NotSupportedException'類型的第一次機會異常
附加信息:LINQ to Entities無法識別方法'System.Linq.IQueryable`1 [typename] GetAllClients()'方法,並且此方法無法轉換為商店表達式。
客戶端是存儲在數據庫中的數據類型。 我正在為常用查詢創建實體框架數據模型的方法,並且由於多租戶設計具有由數據類型定義的安全訪問,我想在數據訪問級別進行過濾。
這里的問題是可查詢層正在嘗試將方法調用GetAllClients
轉換為查詢,而不是使用GetAllClients
的返回值作為查詢源。 語法上的欺騙,是的,但也完全是預期的。
之所以發生這種情況,是因為與IEnumerable
不同, IQueryable
對象實際上提供了可用於將(在這種情況下)轉換為SQL的元代碼。 由於大多數C#方法都沒有SQL等價物,並且無法以相同的方式掃描編譯方法的元序列,因此當這些框架遇到無法翻譯的內容時,它們就會出錯。
請注意,避免大部分問題的一種方法是避免使用Linq styntax,而是進行方法調用,這將稍微減少欺騙:
return GetAllClients()
.Where(item => item.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
.FirstOrDefault();
您正在從過濾器方法返回IQueryable<Client>
。 C#編譯器識別它並自動將 return語句中的lambda表達式轉換為它的表達式樹表示。 (自動轉換是IQueryable的整個延遲執行功能如何工作)。
在第一種情況下, return item from allItems
你會得到一個這樣的表達式樹:
Call: Queryable.Select(Constant: allItems, LambdaExpression: predicate)
在return item from GetAllClients()
的第二種情況下return item from GetAllClients()
你得到這個:
Call: Queryable.Select(Call: GetAllClients, LambdaExpression: predicate)
請注意,Queryable.Select的第一個參數是不同的! 在第二種情況下,編譯器通過將其存儲在表達式樹中來延遲執行對GetAllClients
的調用。 當該表達式樹最終到達EF的SQL轉換器時,EF不知道如何將對C# GetAllClients
函數的調用更改為有效的SQL。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.