Select子句包含非EF方法調用

c# entity-framework linq linq-to-entities postgresql

我在構建實體框架LINQ查詢時遇到問題,該查詢的select子句包含對非EF對象的方法調用。

下面的代碼是用於將數據從一個DBMS轉換為另一個DBMS上的不同模式的應用程序的一部分。在下面的代碼中,Role是我與DBMS無關的自定義類,其他類都是由我的數據庫模式由Entity Framework生成的:

// set up ObjectContext's for Old and new DB schemas
var New = new NewModel.NewEntities();
var Old = new OldModel.OldEntities();

// cache all Role names and IDs in the new-schema roles table into a dictionary
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid);

// create a list or Role objects where Name is name in the old DB, while
// ID is the ID corresponding to that name in the new DB
var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles.ToList();

但是調用ToList會給我這個NotSupportedException:

LINQ to Entities無法識別方法'Int32 get_Item(System.String)'方法,並且此方法無法轉換為存儲表達式

聽起來像LINQ-to-Entities正在調用我的調用,將名稱作為鍵輸出字典。我當然不太了解EF,知道為什麼這是一個問題。

我正在使用devart的dotConnect for PostgreSQL實體框架提供程序,雖然我在這一點上假設這不是DBMS特定的問題。

我知道我可以通過將查詢分成兩個查詢來實現它,如下所示:

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r;
var roles2 = from r in roles.AsEnumerable()
            select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] };
var list = roles2.ToList();

但我想知道是否有更優雅和/或更有效的方法來解決這個問題,理想情況下不會在兩個查詢中將其拆分。

無論如何,我的問題是兩部分:

首先,我可以將此LINQ查詢轉換為Entity Framework將接受的內容,理想情況下不會分成兩部分嗎?

其次,我也很想了解一點EF,所以我可以理解為什麼EF無法在數據庫訪問之上對我的自定義.NET代碼進行分層。我的DBMS不知道如何在Dictionary類上調用一個方法,但是為什麼EF在它已經從數據庫中提取數據之後不能簡單地進行那些Dictionary方法調用?當然,如果我想將多個EF查詢組合在一起並將自定義.NET代碼放在中間,我希望它會失敗,但在這種情況下.NET代碼只是在最後,所以為什麼這是一個問題呢? EF?我假設答案是“這個功能沒有進入EF 1.0”,但我正在尋找更多的解釋為什麼這很難證明它離開EF 1.0。

一般承認的答案

問題在於,在使用Linq的延遲執行時,您必須確定要處理的位置以及要將管道傳輸到客戶端應用程序的數據。在第一個實例中,Linq解析表達式並將所有角色數據作為前導

New.roles.ToDictionary(row => row.rolename, row => row.roleid);

此時,數據從數據庫移動到客戶端並轉換為您的字典。到現在為止還挺好。

問題是你的第二個Linq表達式要求Linq 使用 DB上的字典第二個DB 進行轉換。換句話說,它試圖找出一種方法將整個字典結構傳遞給DB,以便它可以選擇正確的ID值作為查詢延遲執行的一部分。我懷疑如果你把下半場改成了,它會解決得很好

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r.RoleName;
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]);

這樣,它就可以解析您在DB上的選擇(僅選擇角色名稱)作為處理ToDictionary調用的前提(它應該在客戶端上執行,如您所期望的那樣)。這基本上就是你在第二個例子中所做的,因為AsEnumerable在ToList調用中使用它之前將數據提取到客戶端。您可以輕鬆地將其更改為類似的內容

var roles = from rl in Old.userrolelinks
            join r in Old.roles on rl.RoleID equals r.RoleID
            where rl.UserID == userId
            select r;
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] });

它的效果也一樣。對AsEnumerable()的調用解析查詢,將數據拉到客戶端以便在其後面的Select中使用。

請注意,我還沒有對此進行過測試,但就我所理解的實體框架而言,這是我對最新動態的最佳解釋。


熱門答案

雅各布完全正確。如果不將它拆分為兩部分,則無法轉換所需的查詢,因為實體框架無法將get_Item調用轉換為SQL查詢。
唯一的方法是編寫LINQ to Entities查詢,然後將LINQ to Objects查詢寫入其結果,就像Jacob建議的那樣。
問題是特定於實體框架的問題,它不是來自我們對實體框架支持的實現。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
這個KB合法嗎? 是的,了解原因