2011-07-29 47 views
4

我遇到了使用C#和lambdas創建IN子句的問題。C#lambda IN子句錯誤

我有以下方法GetUserList(string filtersByRoles)

可變string filtersByRoles可以容納以逗號分隔的值,例如:「1,2」或「1,2,3」或「1,3,4」等..每個數字表示角色的唯一編號(換句話說,RoleId)。

然後我有下面的C#拉姆達查詢:

var query = _userRepository.GetUserList(); 

它返回一個IQueryable<User>其中User是從我的EntityFramework的表。

一旦我確認如果filtersByRoles參數不是空或空的,我需要做的IN條款如:

if (!string.IsNullOrEmpty(filtersByRoles)) 
{ 
    //Convert *filtersByRoles* to an array of integers 
    int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray(); 

    //Make the IN clause 
    query = query.Where(u => myArray.Contains(u.RoleId)); 
} 

上面的代碼編譯......但在運行時失敗與以下錯誤消息:

LINQ實體無法識別方法 '布爾 包含[的Int32](System.Collections.Generic.IEnumerable`1 [System.Int32], 的Int32)' 方法,和該方法不能被翻譯成st礦石 的表達。


我設法找到一個解決辦法,但它涉及到對的.ToList()方法,我相信獲取所有從我的數據庫,然後將數據呼叫,增加了凡()子句。 但是,這不會打破目的或創造一些性能問題嗎?

這是我做了什麼:

if (!string.IsNullOrEmpty(filtersByRoles)) 
{ 
     string[] myArray = filtersByRoles.Split(','); 
     query = query.ToList().Where(u => myArray.Contains(u.RoleId.ToString())).AsQueryable(); 
} 

我寧願不使.ToList()通話,避免提取的所有數據。

是否有另一種方法來實現這一目標?

編輯: 我正在使用實體框架1.0和.NET Framework 3。5

感謝 真誠

文斯

+0

? –

+0

廢話......對不起... ...框架3.5不是4.0 :-( – Vlince

+0

錯誤是告訴你,linq優化器不知道如何翻譯包含在sql中的相應程序。我從來沒有試過這個,但(u => u.RoleId == myArray [0]).Where(u => u.RoleId == myArray [1])... –

回答

2

這裏是我的2美分:

也許動態的LinQ將幫助您解決問題: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

你可以建立你的位置第一個字符串,這樣說:

string sWhereClause = "1 = 1"; 
foreach(string rId in filtersByRoles.Split(',')) 
    sWhereClause += " OR RoleId = " + rId; 

(我建議,而不是使用+ CONCAT StringBuilder的,但對於這個答案的目的,這並不重要)

然後

query = query.Where(sWhereClause); 

雖然我還沒有嘗試過,但對於解決您的問題聽起來很公平。儘管它看起來像SQL注入...嗯,可以帶來改進。

編輯: 作爲第二個想法我設法來到這個新的想法:

string filterByRoles = "1,2,3"; 
query = query.Where(new Func<User, bool>(u => { 
    return filterByRoles.Contains(u.RoleId.ToString()); 
})).AsQueryable(); 

這樣一來,你要在FUNC {...}代表你可以添加任何代碼,只要它返回一個布爾值(我在這裏假設你的TInput是一個「用戶」類,當然改變它使用與你需要的對應的那個)。

希望這會有所幫助!

+0

現在離開辦公室......但是在週末會試試看,謝謝Flo! – Vlince

0

基於一些你的回覆,我已經設法拉起來是這樣的:

int[] myArray = filtersByRoles.Split(',').Select(x => int.Parse(x)).ToArray(); 
int count = myArray.Count(); 
int role1; 
int role2; 
int role3; 
int role4; 

switch (myArray.Length) 
{ 
    case 1: 
     role1 = myArray[0]; 

     query = query.Where(u => u.RoleId.Equals(role1)); 
     break; 

    case 2: 
     role1 = myArray[0]; 
     role2 = myArray[1]; 

     query = query.Where(u => u.RoleId.Equals(role1) 
           || u.RoleId.Equals(role2)); 
     break; 

    case 3: 
     role1 = myArray[0]; 
     role2 = myArray[1]; 
     role3 = myArray[2]; 

     query = query.Where(u => u.RoleId.Equals(role1) 
           || u.RoleId.Equals(role2) 
           || u.RoleId.Equals(role3)); 
     break; 

    case 4: 
     role1 = myArray[0]; 
     role2 = myArray[1]; 
     role3 = myArray[2]; 
     role4 = myArray[3]; 

     query = query.Where(u => u.RoleId.Equals(role1) 
           || u.RoleId.Equals(role2) 
           || u.RoleId.Equals(role3) 
           || u.RoleId.Equals(role4)); 
     break; 
} 

當直接與myArray的[XXX]想:

query = query.Where(u => u.RoleId.Equals(myArray[0])); 

我得到這個:

LINQ到 實體不支持LINQ表達式節點類型「ArrayIndex」。

因此創建4個整數變量!

現在的作品,但可能需要一些優化...

感謝

您正在使用什麼版本的.NET的