65.9K
CodeProject 正在变化。 阅读更多。
Home

章节摘录 - LINQ 快速入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (6投票s)

2007 年 12 月 7 日

CPOL

10分钟阅读

viewsIcon

30847

本文将介绍如何创建类型化 XML、类型化 XML 支持的功能以及它如何帮助开发。

Packt Publishing 有 2 本新书,书名为

  1. ASP.NET 数据呈现控件精要
  2. 使用 Visual Studio 2005 入门 SQL Server 集成服务指南

引言

LINQ to XSD 通过为非类型化 XML 树添加类型化视图功能来增强 XML 编程。ADO.NET 编程中的 DataSets 也有类似的功能,我们可以使用类型化 DataSets。LINQ to XSD 通过提供从 XML 架构生成的对象模型,提供了更好的编程环境。这被称为类型化 XML 编程

LINQ to XSD 是一个关于类型化 XML 编程的孵化项目。该产品尚未发布。本章中的所有示例和信息均基于此孵化项目,并已在 Visual Studio 2008 Beta 1 中进行了测试。

此 LINQ to XSD 项目应引用 System.Xml.XLinqMicrosoft.Xml.Schema.Linq 库。以下是使用 LINQ 查询访问非类型化 XML 元素的示例。

from c in LoadIcecreams.Elements("Icecream")
select new XElement("Icecream",
c.Element("Price"),
c.Element("Name")));

上面非类型化 XML 的等效类型化 XML 的 LINQ 查询如下所示。

from Icecream in chapter6.Icecream
select new {Icecream.Price, Icecream.Name};

在本章中,我们将介绍如何创建类型化 XML、类型化 XML 支持的功能以及它如何帮助开发。

在 XML 架构中已分配数据类型的 XML 元素称为类型化 XML。XML 解析器使用此数据类型来验证 XML 元素值是否与数据类型匹配。数据类型定义位于同一个 XML 文件中,或者位于单独的架构文件中。

在所有示例中,我们都考虑以下 XML。它包含一个名为http://www.Sample.com/Items的命名空间。XML 包含三种不同冰淇淋的详细信息。XML 的根元素是Chapter6。XML 中的第一行显示了版本和编码等详细信息。

<fixml version="1.0" encoding="utf-8"fi>
<Chapter6 xmlns="http://www.Sample.com/Items">
<Icecream> <!--Chocolate Fudge Icecream-->
   <Name>Chocolate Fudge Icecream</Name>
   <Type>Chocolate</Type>
   <Ingredients>cream, milk, sugar, corn syrup, cellulose gum...
   <Ingredients>
   <Cholestrol>50mg</Cholestrol>
   <TotalCarbohydrates>35g</TotalCarbohydrates>
   <Price>10.5</Price>
   <Protein>
      <VitaminA>3g</VitaminA>
      <Calcium>1g</Calcium>
      <Iron>1g</Iron>
   </Protein>
   <TotalFat>
      <SaturatedFat>9g</SaturatedFat>
      <TransFat>11g</TransFat>
   </TotalFat>
</Icecream>
<Icecream>
   <!--Cherry Vanilla Icecream-->
   <Name>Vanilla Icecream</Name>
   <Type>Vanilla</Type>
   <Ingredients>vanilla extract, guar gum, cream, nonfat milk, sugar, 
locust bean gum, carrageenan, annatto color...</Ingredients>
   <Cholestrol>65mg</Cholestrol>
   <TotalCarbohydrates>26g</TotalCarbohydrates>
   <Price>9.5</Price>
   <Protein>
      <VitaminA>1g</VitaminA>
      <Calcium>2g</Calcium>
      <Iron>1g</Iron>
   </Protein>
   <TotalFat>
      <SaturatedFat>7g</SaturatedFat>
      <TransFat>9g</TransFat>
   </TotalFat> </Icecream> 
<Icecream>
   <!-- Chocolate Icecream-->
   <Name>Banana Split Chocolate Icecream</Name>
   <Type>Chocolate</Type>
   <Ingredients>Banana, guar gum, cream, nonfat milk, sugar, alomnds, raisins,
       honey, locust bean gum, chocolate, annatto color...
   </Ingredients> 
   <Cholestrol>58mg</Cholestrol> 
   <TotalCarbohydrates>24g</TotalCarbohydrates> 
   <Price>11</Price> 
   <Protein> 
      <VitaminA>2g</VitaminA> 
      <Calcium>1g</Calcium> 
      <Iron>1g</Iron> 
   </Protein> 
   <TotalFat> 
      <SaturatedFat>7g</SaturatedFat> 
      <TransFat>6g</TransFat> 
   </TotalFat> 
</Icecream> 
</Chapter6>

非类型化 XML

以下是访问上面显示的非类型化 XML 中数据的示例查询。

XNamespace ns = "http://www.sample.com/Items"; return(
    from icecreams in Items.Elements(ns + "Icecreams")
    from item in icecreams.Elements(ns + "Icecream")
    select item.Element(ns + "Price"),
    item.Element(ns + "Name")
);

该查询使用命名空间ns。此命名空间用于唯一标识 XML 元素。它被添加到查询中使用的所有元素前面。XML 的每个元素都通过Element对象访问。查询中的select语句使用Element对象访问 XML 文档中每个IcecreamPriceName值。

类型化 XML

以下代码是前面部分所示示例 XML 的 XML 架构 (XSD) 合同。此架构定义了 XML 的命名空间、XML 元素的数据类型及其最大和最小出现次数。它还描述了 XML 中每个元素的名称和类型。

<fixml version="1.0" encoding="utf-8" fi>
<xs:schema id="IcecreamsSchema"
   targetNamespace="http://www.Sample.com/Items"
   elementFormDefault="qualified"
   xmlns="http://www.Sample.com/Items"
   xmlns:mstns="http://www.Sample.com/Items"
   xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Chapter6">
   <xs:complexType>
      <xs:sequence>
         <xs:element ref="Icecream"
            minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>
<xs:element name="Icecream">
   <xs:complexType>
      <xs:sequence> 
         <xs:element name="Name" type="xs:string"/> 
         <xs:element name="Type" type="xs:string"/> 
         <xs:element name="Ingredients" type="xs:string"/>
         <xs:element name="Cholestrol" type="xs:string"/>
         <xs:element name="TotalCarbohydrates" type="xs:string"/>
         <xs:element name="Price" type="xs:double"/> 
         <xs:element ref="Protein" minOccurs="0" maxOccurs="1"/>
         <xs:element ref="TotalFat" minOccurs="0" maxOccurs="1"/>
       </xs:sequence>
   </xs:complexType>
</xs:element>
<xs:element name="Protein">
   <xs:complexType>
      <xs:sequence> 
         <xs:element name="VitaminA" type="xs:string"/>
         <xs:element name="Calcium" type="xs:string"/>
         <xs:element name="Iron" type="xs:string"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>
<xs:element name="TotalFat">
   <xs:complexType>
      <xs:sequence> 
         <xs:element name="SaturatedFat" type="xs:string"/>
         <xs:element name="TransFat" type="xs:string"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>
</xs:schema>

LINQ to XSD 会自动从用于 XML 的 XML 架构创建类。这些类提供了 XML 元素的类型化视图。LINQ to XSD 类型的实例称为XML 对象。LINQ to XSD 类的实例是 LINQ to XML 类XElement的实例的包装器。所有 LINQ to XSD 类都有一个公共基类XTypedElement。它包含在Microsoft.Xml.Schema.Lin库中。

public class XTypedElement
{
    private XElement xElement; /* Remaining class definition */
}

元素声明映射到XTypedElement的子类,如下所示。

public class Icecream : XTypedElement
{
    // 


}

这些类包含默认构造函数,以及用于元素和属性的属性。它还提供了LoadSaveClone等方法。当 XML 类型化时,XML 树会立即加载到生成的类的实例中。这里,在查询 XML 元素时不需要对元素进行类型转换。

使用 Visual Studio 创建类型化 XML

Visual Studio 为 LINQ to XSD 功能提供了 IDE 支持。它自动化了架构到类的映射。以下示例基于 LINQ to XSD Preview Alpha 2.0 和 Visual Studio Beta 1。

使用新建项目选项,创建一个LINQ to XSD 控制台应用程序

Screenshot - 2547_ch6_img_1.jpg

使用添加新项对话框,选择在项目下创建 XML 文件的选项。如果您已有 XML 文件,请使用添加现有项选项将其添加到项目中,或将可用 XML 的内容复制粘贴到添加到项目的新 XML 文件中。

Screenshot - 2547_ch6_img_2.jpg

同样,将 XML 架构文件添加到项目中。添加架构文件后,将类型化 XML 部分中的架构内容复制到文件中。

现在我们有了一个 XML 文件和一个 XML 架构文件,但我们需要一个工具来生成架构的对象模型。打开 XML 架构文件的属性窗口,并在生成操作下选择LinqToXsdSchema。这是为了指示工具在生成过程中考虑架构文件。

Screenshot - 2547_ch6_img_3.jpg

对象模型仅在生成过程之后生成。此生成过程还启用了生成类的 IntelliSense,并在对象浏览器窗口中显示信息。要查看对象浏览器,请在 Visual Studio IDE 中选择视图 | 对象浏览器菜单选项。这将显示当前项目中所有对象的层次结构。

Screenshot - 2547_ch6_img_4.jpg

现在,我们可以使用 IntelliSense 以对象方式进行编码,如下面的屏幕截图所示。

Screenshot - 2547_ch6_img_5.jpg

我们还可以使用 IntelliSense 获取对象列表,如下面的屏幕截图所示。

Screenshot - 2547_ch6_img_6.jpg

对象构造

LINQ to XML 提供了一个名为函数式构造的强大功能,该功能能够在单个语句中创建 XML 树。所有属性和元素都列为XElement构造函数的参数。XElement构造函数接受各种类型的参数作为内容。参数可以是XElementXAttribute或对象数组,因此我们可以传递任意数量的对象给XElement。函数式构造功能主要用于导航和修改 XML 树的元素和属性。它实际上是将一种数据形式转换为另一种形式,而不是对其进行操作。

以下代码显示了如何使用函数式构造来构建非类型化 XML 树。在这里,我们使用XElement来构建一个新的Icecream元素并将其添加到现有的Icecream元素中。要添加每个元素,我们必须使用带有元素名称和参数值的XElement

Icecream.Add
(
new XElement("Icecream",
new XElement("Name", "Rum Raisin Ice Cream"),
new XElement("Ingredients", "Rum, guar gum, nonfat milk, cream, 

alomnds,

sugar, raisins, honey, chocolate, annatto color..."),
new XElement("Cholesterol", "49mg"),
new XElement("TotalCarbohydrates", "28g"),
new XElement("Protein", 
new XElement("VitaminA", "2g"),
new XElement("Calcium", "1g"),
new XElement("Iron", "4g")),
new XElement("TotalFat", "16g",
new XElement("SaturatedFat", "5g"),
new XElement("TransFat", "3g"))
)
);

现在,我们将看到如何在不使用XElement的情况下向类型化 XML 添加新元素。我们可以直接使用对象来添加元素。

var newObj = new Icecream 

{ Name = "Rum Raisin Ice Cream", Ingredients = "Rum, guar gum, nonfat milk,
  cream, alomnds, sugar, raisins, honey, chocolate, annatto color...",
  Cholestrol = "49mg", TotalCarbohydrates = "28g", Protein = new Protein 
{VitaminA = "2g", Iron = "4g",
Calcium = "1g"}, Price = 10.25, TotalFat = new TotalFat {SaturatedFat = "5g",
  TransFat = "3g"} 

};
chapter6.Icecream.Add(newObj);

Load 方法

这与我们在 LINQ to XML 中看到的Load方法类似,但区别在于这里的Load方法是 LINQ to XML 的Load方法的类型化版本。下面是一个加载 XML 文件的示例。

var chapter6 = Chapter6.Load("Icecreams.xml");

这里chapter6是一个已经定义的特定类型。在 LINQ to XML 中,非类型化加载可以通过将非类型化加载与所需的类型进行转换来实现类型化。

Screenshot - 2547_ch6_img_7.jpg

在此示例中,您可以看到将Chapter6类型转换为XElement,然后将其用于将 XML 文档加载到chapterSix(这是等效于类型化 XMLchapter6的变量)中。

Parse 方法

此方法是 LINQ to XML 中使用的Parse方法的类型化版本。解析用于将包含 XML 的字符串转换为XElement,然后将该实例转换为所需的类型。下面是一个解析包含 XML 的字符串并将其类型化为Chapter6的示例。

var chapter6Parse = Chapter6.Parse( " <Chapter6 xmlns='http://www.
Sample.com/Items'> <Icecream> " +
" <!--Chocolate Fudge Icecream--> " +
" <Name>Chocolate Fudge Icecream</Name> "+



" <Type>Chocolate</Type> "+
" <Ingredients>cream, milk, sugar, corn syrup, cellulose gum...</
Ingredients> " + 


" <Cholestrol>50mg</Cholestrol> " +
" <TotalCarbohydrates>35g</TotalCarbohydrates>" +
" <Price>10.5</Price> " +
" <Protein> " +
" <VitaminA>3g</VitaminA> " +



" <Calcium>1g</Calcium> " +
" <Iron>1g</Iron> " +
" </Protein> " +
" <TotalFat> " +
" <SaturatedFat>9g</SaturatedFat> " +
" <TransFat>11g</TransFat> " +
" </TotalFat> " +
" </Icecream></Chapter6> " 
);

Save Method

此方法是 LINQ to XML 使用的Save方法的类型化版本。此方法将 XML 树输出为文件、TextWriterXmlWriter

// Save as xml file 


chapter6.Save(@"c:\LINQtoXSDSave.xml");
// or output as TextWriter


 chapter6.Save(TextWriter testTextWriter);
// or output as XmlWriter


 chapter6.Save(XmlWriter testXmlWriter);

上面的代码将chapter6对象中的 XML 树保存到名为LINQtoXSDSave.xml的文件中。

Clone 方法

所有生成的类使用的XTypedElement基类都定义了一个Clone方法。克隆操作的结果是弱类型的,因为克隆应用于底层的非类型化 XML 树。因此,要获得特定类型,在克隆时必须使用类型转换。

// Load xml 


var chapter6 = Chapter6.Load("Icecreams.xml");
// Create a Clone of chapter6 xml


var chapter6Clone = (Chapter6)chapter6.Clone();
var Qry1 = from Icecream in chapter6Clone.Icecream
select new { Icecream.Price, Icecream.Name };
Console.WriteLine(" ");
Console.WriteLine("Clone Sample ");
foreach (var itm in Qry1)
Console.WriteLine("Price of {0} is : {1}", itm.Name , itm.Price); 

在上面的示例中,我们将 XML 加载到chapter6变量中,然后为其创建一个克隆。我们将新克隆的类型转换为Chapter6类型,然后将生成的克隆分配给chapter6Clone。即使我们没有为chapter6Clone分配任何 XML,查询也会产生与应用于chapter6 XML 的查询相同的结果。内部来说,两个对象的 XML 相同,因为chapter6Clone只是chapter6的一个克隆。

默认值

当 XML 树中的 XML 元素值为空时,将为 XML 中的元素返回默认值。这同样适用于属性,但在属性的情况下,它们可能不存在于 XML 树中。默认值在 XML 片段中指定。

<xs:element name="Protein">
   <xs:complexType>
      <xs:sequence>
         <xs:element name="VitaminA" type="xs:string" default="0g"/>
         <xs:element name="Calcium" type="xs:string" default="0g"/>
         <xs:element name="Iron" type="xs:string" default="0g"/>
      </xs:sequence>
   </xs:complexType>
</xs:element>

在上面的示例中,VitaminACalciumIron这三个元素的默认值为0g。因此,如果 XML 树中没有为这些元素指定任何值,则这些元素的结果值将为 0g。

XML 对象的自定义

以下小节解释了 LINQ 中使用的各种自定义类型。

映射时间自定义

有一个配置文件控制 LINQ to XSD 的映射详细信息。XML 命名空间可以映射到 CLR 命名空间。例如,http://www.Sample.com/Items的默认映射将是www.Sample.com.Items。以下示例将http://www.Sample.com/Items映射到LinqToXsdExample.Schema.Items

<Configuration xmlns="http://www.microsoft.com/xml/schema/linq"> 
   <Namespaces> 
    <Namespace Schema="http://www.Sample.com/Items" Clr="LinqToXsdExample.Schema.Items"/>
   </Namespaces>
</Configuration>

这也可用于将嵌套的匿名复杂类型系统地转换为复杂类型定义。

配置文件是

  • 具有指定命名空间的 XML 文件。
  • 由 LINQ to XSD 的命令行处理器使用。
  • 在 Visual Studio 中用于 LINQ to XSD 项目。生成操作可以指定为LinqToXsdConfiguration

我们可以将没有目标命名空间的架构映射到 CLR 命名空间。

编译时间自定义

LINQ to XSD 生成类,并提供可以使用 .NET 进行自定义的对象模型。自定义生成的代码并不容易,因为它需要大量的理解。即使我们自定义了生成的代码,如果代码被重新生成,自定义也会丢失。最佳选择是使用子类化或对partial类进行扩展,通过这种方式我们可以向 LINQ to XSD 生成的类添加方法。

以下是我们chapter6 XML的对象模型,其中chapter6IcecreamProteinTotalFat都生成为类。

Screenshot - 2547_ch6_img_8.jpg

现在我们可以为相应的类创建一个部分类,并添加方法来覆盖基类功能。

LINQ to XSD Visual Studio 项目使用obj\Debug\LinqToXsdSource.cs文件来保存生成的类。

编译后自定义

要在此编译时间自定义类,我们可以使用部分类。如果对象模型是编译过的格式,并且我们没有生成类的源代码,我们可以使用扩展方法向编译过的对象添加行为。可以使用 LINQ to XML 注释功能来实现这一点。

在命令行中使用 LINQ to XSD

有一个名为LinqToXsd.exe的命令行工具,它是一个 XSD 到类的映射器。该工具提供两个选项来转换 XSD。

  • 从 XSD 生成 DLL。
  • 从 XSD 生成 .cs 文件,这是默认选项。

例如,以下是从 LINQ to XSD 安装目录中的 XSD 生成 DLL 的命令。

LinqToXsd.exe Items.xsd /lib: Items.dll

摘要

LINQ_Quickly.jpg

在本章中,我们了解了 LINQ to XSD 将提供的各种功能。我们还看到了 LINQ to XSD 支持的一些功能的示例。通过将非类型化 XML 转换为类型化 XML,这使程序员的工作更加轻松。LINQ to XSD 为 XML 元素生成类,并提供一个对象模型,程序员可以直接访问该模型,就像访问 .NET 对象一样。

© . All rights reserved.