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

ASP.NET
FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用方法
.NET 常用功能和代码小结
在 .NET Framework 2.0 中未处理的异常导致基于 ASP.NET 的应用程序意外退出
asp.net IList查询数据后格式化数据再绑定控件
asp.net sql存储过程
asp.net 简单实现禁用或启用页面中的某一类型的控件
asp.net(c#)获取内容第一张图片地址的函数
The remote procedure call failed and did not execute的解决办法
ASP.NET 在线文件管理
asp.net 读取并修改config文件实现代码
ASP.NET Cookie 操作实现
asp.net Silverlight中的模式窗体
Silverlight中动态获取Web Service地址
asp.net Silverlight应用程序中获取载体aspx页面参数
asp.net 水晶报表隔行换色实现方法
asp.net 获取Gridview隐藏列的值
手动把asp.net的类生成dll文件的方法
asp.net 使用ObjectDataSource控件在ASP.NET中实现Ajax真分页
动态指定任意类型的ObjectDataSource对象的查询参数
asp.net Md5的用法小结

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


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