2009-08-26 62 views
0

previous question中,我詢問了如何邏輯分組XML元素,然後我得到了答案,即嵌套Linq查詢。Linq/XML:在XML元素內正確分組結果 - 使用內部聯接!

問題是,這具有左連接嵌套查詢的效果。例如,假設我想列出在美國以字母「Y」開頭,由國家分組的所有城市和縣:

XElement xml = new XElement("States", 
    from s in LinqUtils.GetTable<State>() 
    orderby s.Code 
    select new XElement("State", 
    new XAttribute("Code", s.Code), 
    new XAttribute("Name", s.Name), 
    from cy in s.Counties 
    orderby cy.Name 
    select new XElement("County", 
     new XAttribute("Name", cy.Name), 
     from c in cy.Cities 
     where c.Name.StartsWith("Y") 
     orderby c.Name 
     select new XElement("City", 
     new XAttribute("Name", c.Name) 
    ) 
    ) 
) 
); 

Console.WriteLine(xml); 

此輸出:

<States> 
    <State Code="AK" Name="Alaska "> 
    <County Name="ALEUTIANS EAST" /> 
    <County Name="ALEUTIANS WEST" /> 
    <County Name="ANCHORAGE" /> 
    <County Name="BETHEL" /> 
    ... 
    <County Name="YAKUTAT"> 
     <City Name="YAKUTAT" /> 
    </County> 
    <County Name="YUKON KOYUKUK" /> 
    </State> 
    <State Code="AL" Name="Alabama "> 
    <County Name="AUTAUGA" /> 
    ... 
    etc. 

我不要求左連接效果;我只想看看實際上包含以字母「Y」開頭的城市的州和縣。

我可以想出幾種方法來做到這一點,但他們都顯得很笨拙和不雅。爲了達到預期效果,您可以想到什麼是最簡單的方法?

回答

2

有幾種方法可以解決這個問題,但沒有一個是非常優雅的。有幾個選項:

方案1:let捕獲子查詢並過濾掉空值:

XElement xml = new XElement("States", 
    from s in LinqUtils.GetTable<State>() 
    let counties = from cy in s.Counties 
       let cities = from c in cy.Cities 
           where c.Name.StartsWith("Y") 
           orderby c.Name 
           select new XElement("City", 
           new XAttribute("Name", c.Name) 
          ) 
       where cities.Any() 
       orderby cy.Name 
       select new XElement("County", 
        new XAttribute("Name", cy.Name), 
        cities   
       ) 
    where counties.Any() 
    orderby s.Code 
    select new XElement("State", 
    new XAttribute("Code", s.Code), 
    new XAttribute("Name", s.Name), 
    counties 
) 
); 

方案2:通過,而不是不同的使用帶有組內加入方法:

XElement xml = new XElement("States", 
    from s in LinqUtils.GetTable<State>() 
    from cy in s.Counties 
    from c in cy.Cities 
    where c.Name.StartsWith("Y") 
    group new { cy, c } by s into gs 
    let s = gs.Key 
    orderby s.Code 
    select new XElement("State", 
    new XAttribute("Code", s.Code), 
    new XAttribute("Name", s.Name), 

    from g in gs 
    group g.c by g.cy into gcy 
    let cy = gcy.Key 
    orderby cy.Name 
    select new XElement("County", 
     new XAttribute("Name", cy.Name), 

     from c in gcy 
     orderby c.Name 
     select new XElement("City", 
     new XAttribute("Name", c.Name) 
    ) 
    ) 
) 
); 
+0

非常好!感謝您向我展示如何使用匿名類型進行嵌套分組 - 這會讓您獲得答案功勞! :) –

0

以下是一種方法:首先使用所有正確的內部聯接創建查詢,然後使用Distinct()過濾器創建外部分組,然後使用where子句從分組中創建XML以加入它們。因此:

var Cities = from s in LinqUtils.GetTable<State>() 
      from cy in s.Counties 
      from c in cy.Cities 
      where c.Name.StartsWith("Y") 
      select c; 

var States = Cities.Select(c => c.County.State).Distinct(); 
var Counties = Cities.Select(c => c.County).Distinct(); 

XElement xml = new XElement("States", 
    from s in States 
    orderby s.Code 
    select new XElement("State", 
    new XAttribute("Code", s.Code), 
    new XAttribute("Name", s.Name), 
    from cy in Counties 
    where cy.StateCode == s.Code 
    orderby cy.Name 
    select new XElement("County", 
     new XAttribute("Name", cy.Name), 
     from c in Cities 
     where c.CountyID == cy.ID 
     orderby c.Name 
     select new XElement("City", 
     new XAttribute("Name", c.Name) 
    ) 
    ) 
) 
); 

它的工作原理,但不知何故,我必須有一個更好的辦法的感覺...

1

我認爲你有一個良好的開端。您可以將Cities列表中的國家和州的信息添加到group by列表中,並避免第二次加入和過濾。
你甚至可以在一個大的LINQ查詢中做到這一點。這是很難正是你需要的,因爲你有你自己的類,但這裏的文件和文件夾類似的東西寫(你需要添加另一個級別):

dirs = new List<DirectoryInfo>(); 
dirs.Add(new DirectoryInfo("c:\\")); 
dirs.Add(new DirectoryInfo("c:\\windows\\")); 

var a = from directory in dirs 
     from file in directory.GetFiles() 
     where file.Name.StartsWith("a") 
     group file by directory.Name into fileGroup 
     select new XElement("Directory", new XAttribute("path", fileGroup.Key), 
      from f in fileGroup 
      select new XElement("File", f.Name) 
      ); 

XDocument doc = new XDocument(new XElement("Folders", a)); 

在XML得到的:

<Folders> 
    <Directory path="c:\"> 
    <File>ActiveDirectoryService.cs</File> 
    <File>ApplicationTemplateCore.wsp</File> 
    <File>AUTOEXEC.BAT</File> 
    </Directory> 
    <Directory path="windows"> 
    <File>adfs.msp</File> 
    <File>adminscript2nd.exe</File> 
    <File>aspnetocm.log</File> 
    </Directory> 
</Folders> 

同樣,這裏的關鍵是對結果使用group by

+0

戶田拉巴......但是我很困難與雙重嵌套的羣。您能否舉一個例子來說明如何進行雙重嵌套,例如州 - >縣 - >城市?謝謝! –