程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> LINQ學習筆記:投射到X-DOM

LINQ學習筆記:投射到X-DOM

編輯:關於.NET

投射到X-DOM

我們可以將LINQ查詢投射到一個X-DOM. 其數據源可以是LINQ支持的任何一種, 例如:

  • LINQ to SQL 表
  • 本地集合
  • 另外一個X-DOM

不管是那種數據源, 使用LINQ投射一個X-DOM的策略是一樣的: 你首先需要編寫一個構建表達式用於產生需要的X-DOM形狀, 然後圍繞這個表達式編寫LINQ查詢

例如, 假設我們想從一個數據庫當中查詢客戶並產生相應的XML:

   1: <customers>
   2:   <customer id="1">
   3:     <name>Sue</name>
   4:     <buys>3</buys>
   5:   </customer>
   6: </customers>

我們開始使用簡單的文字為該X-DOM編寫一個功能性的構造表達式:

   1: var customers =
   2:   new XElement ("customers",
   3:     new XElement ("customer", new XAttribute ("id", 1),
   4:       new XElement ("name", "Sue"),
   5:       new XElement ("buys", 3)
   6:     )
   7:   );

然後我們將其轉換成為一個影射創建LINQ查詢:

   1: var customers =
   2:   new XElement ("customers",
   3:     from c in dataContext.Customers
   4:     select
   5:       new XElement ("customer",
   6:         new XAttribute ("id", c.ID),
   7:         new XElement ("name", c.Name),
   8:         new XElement ("buys", c.Purchases.Count)
   9:       )
  10:     );

最後的結果可能類似:

   1: <customers>
   2:   <customer id="1">
   3:     <name>Tom</firstname>
   4:     <buys>3</buys>
   5:   </customer>
   6:   <customer id="2">
   7:     <name>Harry</firstname>
   8:     <buys>2</buys>
   9:   </customer>
  10:     ...
  11: </customers>

在這個例子中外部的查詢使得查詢從遠程的LINQ to SQL轉換成了本地的可枚舉查詢. XElement的構造器並不知道IQueryable<>, 因此它將導致LINQ to SQL立即執行SQL語句.

消滅空元素

假設前面的例子我們還想要包括客戶最近的高價值的采購單的信息, 我們可以這樣做:

   1: var customers =
   2:   new XElement ("customers",
   3:     from c in dataContext.Customers
   4:     let lastBigBuy = (from p in c.Purchases
   5:                      where p.Price > 1000
   6:                      orderby p.Date descending
   7:                      select p).FirstOrDefault()
   8:     select
   9:       new XElement ("customer",
  10:         new XAttribute ("id", c.ID),
  11:         new XElement ("name", c.Name),
  12:         new XElement ("buys",c.Purchases.Count),
  13:         new XElement ("lastBigBuy",
  14:           new XElement("description",
  15:             lastBigBuy == null
  16:               ? null: lastBigBuy.Description),
  17:           new XElement("price",
  18:             lastBigBuy == null
  19:               ? 0m :lastBigBuy.Price)
  20:         )
  21:       )
  22:   );
  23:

這會去掉空的元素, 也就是那些沒有高價值采購單的客戶. (如果它是一個本地查詢, 而不是LINQ to SQL查詢, NullReferenceException異常將會拋出. 在這個例子中, 整個省略lastBigBuy節點會更好. 我們可以通過在條件操作符裡面包裝一個lastBigBuy的構造器來完成這個目標)

   1: select
   2:   new XElement ("customer",
   3:     new XAttribute ("id", c.ID),
   4:     new XElement ("name", c.Name),
   5:     new XElement ("buys", c.Purchases.Count),
   6:     lastBigBuy == null ? null :
   7:       new XElement ("lastBigBuy",
   8:         new XElement ("description",
   9:           lastBigBuy.Description),
  10:         new XElement ("price", lastBigBuy.Price)

對於那些沒有lastBigBuy的客戶, null將會被發出而不是XElement. 這也是我們所想要的因為null的內容通常都是被忽略的.

流化一個投射

如果你正在通過調用Save來投射一個X-DOM, 你可以使用XStreamingElement來提高內存的效率. XStreamingElement是一個削減過的XElement版本, 對其子內容使用了延遲加載.要使用它, 你可以簡單的使用XStreamingElement來替換外圍的XElement:

   1: var customers =
   2:   new XStreamingElement ("customers",
   3:     from c in dataContext.Customers
   4:     select
   5:       new XStreamingElement ("customer",
   6:         new XAttribute ("id", c.ID),
   7:         new XElement ("name", c.Name),
   8:         new XElement ("buys", c.Purchases.Count)
   9:       )
  10:     );
  11: customers.Save ("data.XML");

這個查詢將通過XStreamingElement的構造器並且不會被執行直到你在Element上面調用了Save, ToString或者WriteTo; 這避免了一次將整個X-DOM加載到內存當中. 另外一點是該查詢還會自動判別是否是重新Save, 你也不能橫貫XStreamingElement的子內容——因為它並沒有暴露類似Elements或者Attribute的方法.

XStreamingElement是基於XObject的——而不是其他類——因為它有一些有限的成員. 除了Save, ToString和WriteTo之外其他的成員就是:

  • Add方法, 其接受類似構造器的內容
  • Name屬性

XStreamingElement不允許按流行的方法讀取流的內容——為了達到這個目標, 你必須和X-DOM一起使用XMLReader.

轉換X-DOM

我們可以通過重新投影來轉換一個X-DOM. 例如, 假設我們想要轉換一個msbuild的XML文件到一個簡單的格式以便可以用其產生一個報表. 一個mubuild文件看起來類似這樣:

   1: <Project DefaultTargets="Build"
   2:   XMLns="http://schemas.microsoft.com/dev...>
   3:   <PropertyGroup>
   4:     <Platform Condition=" '$(Platform)' == '' ">
   5:       AnyCPU
   6:     </Platform>
   7:     <ProductVersion>9.0.11209</ProductVersion>
   8:     ...
   9:   </PropertyGroup>
  10:   <ItemGroup>
  11:     <Compile Include="ObjectGraph.cs" />
  12:     <Compile Include="Program.cs" />
  13:     <Compile Include="PropertIEs\AssemblyInfo.cs" />
  14:     <Compile Include="Tests\Aggregation.cs" />
  15:     <Compile Include="Tests\Advanced\RecursiveXML.cs" />
  16:   </ItemGroup>
  17:   <ItemGroup>
  18:     ...
  19:   </ItemGroup>
  20:    ...
  21: </Project>

假設我們只想包含文件, 如下:

   1: <ProjectReport>
   2:   <File>ObjectGraph.cs</File>
   3:   <File>Program.cs</File>
   4:   <File>PropertIEs\AssemblyInfo.cs</File>
   5:   <File>Tests\Aggregation.cs</File>
   6:   <File>Tests\Advanced\RecursiveXML.cs</File>
   7: </ProjectReport>

以下的查詢執行了這個變換:

   1: XElement project = XElement.Load("myProjectFile.csproj");
   2: XNamespace ns = project.Name.Namespace;
   3: var query =
   4:   new XElement ("ProjectReport",
   5:     from compileItem in
   6:       project.Elements (ns + "ItemGroup")
   7:              .Elements (ns + "Compile")
   8:     let include = compileItem.Attribute ("Include")
   9:     where include != null
  10:     select new XElement ("File", include.Value)
  11:   );

此查詢提取了所有的ItemGroup元素, 然後使用Elements的擴展方法去獲取一個扁平的包含所有Compile子元素的序列. 注意我們必須要指定一個XML命名空間——原來的文件裡面所有的東西都繼承了Project元素中定義的命名空間——因此本地元素例如ItemGroup 並不會自動生成一樣的命名空間. 我們還提取了Include屬性並將其投射到一個元素上面. (全序列完!)

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved