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

ASP.NET
赫赫大名的A*寻路算法(vb.net版本)
asp.net(c#)下Jmai去说明 使用与下载
[原创]完美解决Could not load file or assembly ''AjaxPro.2'' or one of its dependencies. 拒绝访问。
asp.net下gridview 批量删除的实现方法
用CSS实现图片倾斜 只支持IE
.net get set用法小结
vs 不显示行号的操作方法
ASP.NET页面进行GZIP压缩优化的几款压缩模块的使用简介及应用测试!(附源码)
ASP.Net不执行问题一解
asp.net 无限分类
让VS2008对JQuery语法的智能感知更完美一点
扩展方法ToJSON() and ParseJSON()
asp.net下PageMethods使用技巧
Linq to SQL Delete时遇到问题的解决方法
实现ASP.NET多文件上传程序代码
ASP.NET AJAX 1.0 RC开发10分钟图解
asp.net get set用法
ASP.NET下使用WScript.Shell执行命令
asp.net2.0实现邮件发送(测试成功)
Asp.net 无限级分类实例代码

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


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-28   浏览: 73 ::
收藏到网摘: 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属性并将其投射到一个元素上面. (全序列完!)