2012-06-07 89 views
2

讓我首先說我已經在這裏搜索和閱讀了很多關於動態where子句的線程,以及來自Albahari的ScottGu博客和PredicateBuilder類,但是我不是確定我可以如何正確地將這些方法應用於我的案例。不知何故,我無法把頭圍住它。Linq多級動態where子句

我有下面的代碼,完成「靜態」像這樣當其工作原理:

var persons = from father in fathers 
       select new         
       { 
       Count = father.Sons 
            .Select(son => son) 
            .Where(son => son.Skills.Any(skill => skill.SkillType == "Languages" && skill.Name == "French")) 
            .Where(son => son.Skills.Any(skill => skill.SkillType == "Sport" && skill.Name == "Football")) 
            .Count(), 

       Name = father.Name 
       }; 

不過,我想有在運行時所產生的where子句。父對象具有Son對象的集合,而Son對象又擁有一組技能對象。如查詢所示,我想知道每個父親的姓名以及他們擁有一定技能的兒子的人數。這組技能將在運行時被選中,所以即使在這個例子中,我們只有兩組技能(2 where子句),它可以是10或任意數量的子句。

我認爲我最大的問題是,我似乎無法調整這裏給出的關於StackOverflow的答案給我的情況,因爲我需要從頂層(父親)獲取信息以及來自關於第二級(兒子)信息的第三級(技能)。

如果需要,我會發布明天我已經嘗試過的示例代碼。我現在做不了,因爲我有點急。任何幫助將非常感激。

編輯:

我需要的是,以連接where子句在運行時間取決於有多少過濾標準已經由用戶選擇的方式。不同的過濾條件是從外部來源獲得的,並在運行時建立。例如:

在方案一中,可能有3個標準,例如用戶選擇的標準1,標準2和標準3。情景2可以有5個標準,比如標準1,標準2,...,標準5。第三種情況可能有10個標準,例如標準1,標準2,...,標準10。我需要的是能夠針對每種情況執行以下操作,而無需事先知道是否有3,5,10或任何標準。

方案之一:

var persons = from father in fathers 
       select new         
       { 
       Count = father.Sons 
            .Select(son => son) 
            .Where(criteria1) 
            .Where(criteria2) 
            .Where(criteria3) 
            .Count(), 

       Name = father.Name 
       }; 

方案2:

var persons = from father in fathers 
       select new         
       { 
       Count = father.Sons 
            .Select(son => son) 
            .Where(criteria1) 
            .Where(criteria2) 
            .Where(criteria3) 
            .Where(criteria4) 
            .Where(criteria5) 
            .Count(), 

       Name = father.Name 
       }; 

方案3:

var persons = from father in fathers 
        select new         
        { 
        Count = father.Sons 
             .Select(son => son) 
             .Where(criteria1) 
         .Where(criteria2) 
         .Where(criteria3) 
         .Where(criteria4) 
         .Where(criteria5)     
         .Where(criteria6) 
         .Where(criteria7) 
         .Where(criteria8) 
         .Where(criteria9) 
         .Where(criteria10) 
            .Count(), 
        Name = father.Name 
        }; 
+0

是'父'IQueryable'還是'IEnumerable'? – smartcaveman

+0

@smartcaveman我可以兩種方式,它留給我。這有什麼不同嗎? – FonTak

+0

是的。答案取決於它。 – smartcaveman

回答

2

通過jonnyGold答案是好的,但它需要子對象具有對父對象的引用。這是一個不需要的解決方案:

var query = from father in fathers 
      from son in father.Sons 
      select new {father, son}; 

foreach (Skill skillCriterion in CriterionSkills) 
{ 
    var capturedSkillCriterion = skillCriterion; 
    query = query.Where(fs => fs.son.Skills.Any(
     skill => skill.SkillType == capturedSkillCriterion.SkillType && 
       skill.Name == capturedSkillCriterion.Name));   
}   

var persons = from fs in query 
       group fs by fs.father into g 
       select new         
       { 
       Count = g.Count(), 
       Name = g.Key.Name 
       }; 
+0

在閱讀您的修改之後,我更喜歡這個解決方案。 @FonTak - 使用這一個。 – bluevector

+0

@kris Vandermotten非常感謝您的解決方案。這非常接近。當我試圖理解你的代碼時,我看不出有什麼理由不應該這樣做。但是,在試圖在一些示例數據上運行它之後,它的行爲就好像篩選條件是OR,而不是AND。所以如果一個父親有三個兒子,其中兩個知道C#和java,而第三個知道C#,如果我試圖找到知道C#和Java的兒子的數量,我會得到3個回報,而不是2個我會繼續研究爲什麼會發生這種情況,但只是想提一提,不如說明白爲什麼會發生這種情況。 – FonTak

+0

可能是一個閉包bug中着名的foreach變量。我改變了代碼。 –

1
var sons = fathers.SelectMany(f => f.Sons); 

foreach(Skill skillCriterion in CriterionSkills) 
{ 
    sons = sons.Where(son => son.Skills.Any(skill => skill.SkillType == skillCriterion.SkillType && skill.Name == skillCriterion.Name)); 
} 

// we need to assume some sort of formal father-son relationship 
var persons = from son in sons 
       group son by new {son.Father.ID, son.Father.Name} into g 
       select new 
       { 
       g.Key.Name, 
       g.Count() 
       }; 
+0

感謝您的建議,但我不知道可供用戶選擇的標準數量。過濾標準將在運行時從外部源動態構建,因此我不能使用if(includeSpeaksFrench)或if(includePlaysFootbal)這樣的條件,因爲這需要我事先知道有「講法語」或一個「踢足球」的標準。我將編輯我的原始帖子以包含此信息。 – FonTak

+0

然後循環你所擁有的...我編輯了我的答案。 – bluevector

+0

非常好的答案,除了父子關係假設。看到我的回答,那不需要那種關係。 –

0

然後設計你的查詢在哪裏處理過濾器的dyanmic特性。例如,我創造了兒子可以踢足球或排球的父親和兒子。現在我的查詢將採取的dyanmic要求說足球只是...這是我要做的事:

void Main() 
{ 
    bool findFootballers = true; 
    bool findVolleyBallers = false; 

    var Fathers = new List<Father>() 
    { 
     new Father() { Name = "Frank SR", Sons = new List<Son>() { new Son() { Name = "Bob", PlaysFootball = true }, new Son() { Name = "Frank", PlaysVolleyBall = true } } }, 
     new Father() { Name = "Knute", Sons = new List<Son>() { new Son() { Name = "Mean Jo Green", PlaysFootball = true }, new Son() { Name = "McMann", PlaysFootball = true } } } 
    }; 


    Fathers.Where (f => (findFootballers == false) ? true : f.Sons.Any (s => s.PlaysFootball == true)) 
      .Where (f => (findVolleyBallers == false) ? true : f.Sons.Any (s => s.PlaysVolleyBall == true)) 
      .Select(f => new 
         { 
         Name = f.Name, 
         TargetSportSons = string.Join(", ", f.Sons 
                  .Where (s => (findFootballers == false) ? true : s.PlaysFootball) 
                  .Where (s => (findVolleyBallers == false) ? true : s.PlaysVolleyBall) 
                  .Select (s => s.Name)) 
         } 
       ) 
      .ToList() 
      .ForEach(fs => Console.WriteLine ("Father {0} has these sons {1} who play {2}", fs.Name, fs.TargetSportSons, (findFootballers ? "Football" : "VolleyBall "))); 

// Output 
// Father Frank SR has these sons Bob who play Football 
// Father Knute has these sons Mean Jo Green, McMann who play Football 

} 

public class Son 
{ 
    public string Name { get; set; } 
    public bool PlaysFootball { get; set; } 
    public bool PlaysVolleyBall { get; set;} 
} 


public class Father 
{ 
    public string Name { get; set; } 
    public List<Son> Sons = new List<Son>(); 

} 

// Define other methods and classes here 
+0

感謝您的建議。我想我的問題沒有說清楚,但是在設計時,我不知道可供用戶選擇的標準數量。過濾條件將從外部源動態構建,所以我害怕我不能使用你的方法,因爲它確實要求我在設計時知道我將使用的linq語句中有多少「.where」部分。不知道我已經很好地解釋了我的問題。我會編輯我的第一篇文章,以更好地解釋這一點。 – FonTak

+0

@FonTak我聽到你在說什麼,但是你必須有一個針對數據庫的數據上下文嗎?因此,過濾器的實際數量僅限於數據庫中的內容。我的系統通過所有可能的過濾器組合來適用於該場景。我會重新閱讀你的文章,看看我是否能夠找到任何見解,以更動態的方法。 – OmegaMan

+0

實際上,數據不是來自數據庫。截至目前,我不知道數據將來自何處。我只知道,當我得到這些數據時,我會將它歸類爲技能類型和技能名稱。據我所知,它可能來自一個始終可以擴展的XML文件。 – FonTak