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

ASP.NET
如何在ASP.NET中使用SmtpMail发送邮件
在VB.NET中利用Split和Replace函数计算字数
Attribute应用:简化ANF自定义控件初始化过程
ASP.NET 2.0移动开发入门之使用样式
ASP.NET 2.0中使用OWC生成图表
ASP.NET 2.0中控件的简单异步回调
一个无法捕获ADO.NET Dataset的内存错误
深入解读ADO.NET2.0的十大最新特性
.Net平台下的分布式缓存设计
ASP.NET全局异常处理浅析
ASP.NET 2.0中文验证码的实现
浅析.NET平台编程语言的未来走向
.net 框架程序设计收藏
使用ASP.NET MVC Futures 中的异步Action
详解.NET中的XmlReader与XmlWriter
关于.NET中的Server push技术
asp.net页面执行机制
对比JSP和ASP.NET的存储过程
.NET 4.0不会包含System.Shell.CommandLine
ASP.NET十个有效性能优化的方法

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


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