当前位置: 首页 > 图文教程 > 网络编程 > ASP.NET > LINQ学习笔记:投射到X-DOM

ASP.NET
LINQ学习笔记:元素操作符
LINQ学习笔记:聚合方法
LINQ学习笔记:量词
LINQ学习笔记:创建方法
LINQ学习笔记:LINQ to XML
LINQ学习笔记:X-DOM概览
LINQ学习笔记:实例化一个X-DOM
LINQ学习笔记:导航和查询X-DOM
LINQ学习笔记:更新一个X-DOM
LINQ学习笔记:string类型的Value属性
LINQ学习笔记:XML命名空间
LINQ学习笔记:投射到X-DOM
C#教程:声明和调用扩展方法
C#教程:没有扩展方法的日子
C#教程:lambda表达式转换成表达式树
C#教程:编程方式构建表达式树
C#教程:使用lambda表达式记录事件日志
C#教程:作为委托的Lambda表达式
C#教程:关于匿名类型的特性
C#教程:隐式类型数组

ASP.NET 中的 LINQ学习笔记:投射到X-DOM


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-28   浏览: 34 ::
收藏到网摘: n/a

投射到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属性并将其投射到一个元素上面. (全序列完!)