首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

21.6  XML模式

XML已经在ADO.NET确立了牢固的地位—— 实际上,现在在对象之间远程传递数据的格式是XML。有了.NET运行库,就可以在XML模式定义文件(XSD)中描述DataTable了。而且,可以定义整个DataSet,其中有许多DataTables,这些表之间有一定的关系,并包括描述数据的其他信息。

在定义XSD文件时,运行库中有一个新工具,该工具可以把这个模式转换为对应的数据访问类,例如类型安全的产品DataTable类,如上所述。本节开始介绍一个简单的XSD文件,该文件描述与前面产品示例相同的信息,再扩展它使其包括一些额外的功能。

<?xml version="1.0" encoding="utf-8" ?>

<xs:schema

   id="Products"

   targetNamespace="http://tempuri.org/XMLSchema1.xsd"

   xmlns:mstns="http://tempuri.org/XMLSchema1.xsd"

   xmlns:xsd="http://www.w3.org/2001/XMLSchema"

   xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

  <xs:element FTEL="Product">

    <xs:complexType>

      <xs:sequence>

        <xs:element FTEL="ProductID" msdata:ReadOnly="true"

msdata:AutoIncrement="true" type="xs:int" />

        <xs:element FTEL="ProductName" type="xs:string" />

        <xs:element FTEL="SupplierID" type="xs:int" minOccurs="0" />

        <xs:element FTEL="CategoryID" type="xs:int" minOccurs="0" />

        <xs:element FTEL="QuantityPerUnit" type="xs:string" minOccurs="0" />

        <xs:element FTEL="UnitPrice" type="xs:decimal" minOccurs="0" />

        <xs:element FTEL="UnitsInStock" type="xs:short" minOccurs="0" />

        <xs:element FTEL="UnitsOnOrder" type="xs:short" minOccurs="0" />

        <xs:element FTEL="ReorderLevel" type="xs:short" minOccurs="0" />

        <xs:element FTEL="Discontinued" type="xs:boolean" />

      </xs:sequence>

    </xs:complexType>

  </xs:element>

</xs:schema>

24章将详细论述其中的一些选项。现在应知道,这个文件定义一个带有id属性的模式,其中把id属性设置为Products。还定义了一个比较复杂的类Product,其中包含了许多元素,每个元素对应于Products表中的一个字段。

这些元素映射到数据类上。其中Products模式映射到派生于DataSet的一个类上,类Product映射到派生于DataTable的一个类上。每个子元素映射到派生于DataColumn的一个类上。所有列的集合都映射到派生于DataRow的一个类上。

.NET Framework中有一个工具,只要输入XSD文件,该工具就可以生成这些类的代码。因为该工具惟一的工作是执行XSD文件上的各种功能,所以它称为XSD.EXE

XSD生成代码

假定把上面的文件保存为Product.xsd,在命令提示符上输入下述命令,把该文件转换为   代码:

xsd Product.xsd /d

这会创建文件Product.cs

该命令有许多选项可以和XSD一起使用,以改变生成的输出结果,其中一些比较常用的选项如表21-8所示。

 

 

  21-8

   

   

/dataset (/d)

生成派生于DataSetDataTableDataRow的类

/language:<language>

允许选择编写输出文件的语言。C#是默认语言,也可以选择用VB编写Visual Basic文件

/namespace:<namespace>

定义存储生成代码的命名空间,默认为没有命名空间

 

下面节选了products模式在XSD中的输出结果,并做了略微的修改,重新进行了格式化,以便可以放在本书中。要查看完整的输出结果,可以在products模式(或者自己建立的某个模式)上运行XSD.EXE,查看生成的.cs文件。该示例包含了所有的源代码和Products.XSD文件,这个输出结果是www.wroc.com上下载代码文件的一部分。

//------------------------------------------------------------------------------

// <autogenerated>

//     This code was generated by a tool.

//     Runtime Version: 11.4322.573

//

//     Changes to this file may cause incorrect behavior and will be lost if

//     the code is regenerated.

// </autogenerated>

//------------------------------------------------------------------------------

 

//

// This source code was auto-generated by xsd, Version=11.4322.573

//

using System;

using System.Data;

using System.Xml;

using System.Runtime.Serialization;

 

[Serializable()]

[System.ComponentModel.DesignerCategoryAttribute("code")]

[System.Diagnostics.DebuggerStepThrough()]

[System.ComponentModel.ToolboxItem(true)]

public class Products : DataSet

{

   private ProductDataTable tableProduct;

   public Products()

   public ProductDataTable Product

   public override DataSet Clone()

   public delegate void ProductRowChangeEventHandler ( object sender,

                                                 ProductRowChangeEvent e);

 

   [System.Diagnostics.DebuggerStepThrough()]

   public class ProductDataTable : DataTable, System.Collections.IEnumerable

 

   [System.Diagnostics.DebuggerStepThrough()]

   public class ProductRow : DataRow

}

为了集中论述公共接口,这段代码删除了所有受保护和私有的成员。ProductDataTableProductRow定义是两个嵌套的类,后面会介绍它们。下面简单地解释一下DataSet派生类,之后讨论这些类的代码。

Products()构造函数调用一个私有方法InitClass(),构造派生于DataTable类的ProductDataTable的一个实例,把该表添加到DataSetTables集合上。Products数据表可以通过下面的代码来访问:

DataSet ds = new Products();

DataTable products = ds.Tables["Products"];

更简单的方式是使用Product属性来访问,该属性在派生的DataSet对象上:

DataTable products = ds.Product;

因为Product属性是强类型化的,所以可以使用ProductDataTable,而不是上面的DataTable引用。

ProductDataTable类包含更多的代码:

[System.Diagnostics.DebuggerStepThrough()]

public class ProductDataTable : DataTable, System.Collections.IEnumerable

{

   private DataColumn columnProductID;

   private DataColumn columnProductName;

   private DataColumn columnSupplierID;

   private DataColumn columnCategoryID;

   private DataColumn columnQuantityPerUnit;

   private DataColumn columnUnitPrice;

   private DataColumn columnUnitsInStock;

   private DataColumn columnUnitsOnOrder;

   private DataColumn columnReorderLevel;

   private DataColumn columnDiscontinued;

        

   internal ProductDataTable() : base("Product")

   {

      this.InitClass();

  }

ProductDataTable类派生于DataTable,它实现IEnumerable接口,为表中的每一列定义了一个私有的DataColumn实例,通过调用私有的InitClass成员,在构造函数中初始化这些实例。每一列都有一个内部的访问器,DataRow类将使用这个访问器(后面介绍)

[System.ComponentModel.Browsable(false)]

public int Count

{

   get { return this.Rows.Count; }

}

internal DataColumn ProductIDColumn

{

   get { return this.columnProductID; }

}

// Other row accessors removed for clarity – there is one for each of the columns

给表添加数据行是由AddProductRow()的两个重载方法实现的(但它们的内容完全不同,但名称相同)。第一个重载方法的参数是一个已经构造出来的DataRow,且没有返回值。另一个重载方法则带一组参数值,每个参数对应于DataTable中的一列,该重载方法构造一个新行,设置这个新行中的值,把该行添加到DataTable,并给调用者返回该行。这些不同的函数不应有相同的名称。

public void AddProductRow(ProductRow row)

{

   this.Rows.Add(row);

}

 

public ProductRow AddProductRow ( string ProductName , int SupplierID ,

                                  int CategoryID , string QuantityPerUnit ,

                                  System.Decimal UnitPrice , short UnitsInStock ,

                                  short UnitsOnOrder , short ReorderLevel ,

                                  bool Discontinued )

{

   ProductRow rowProductRow = ((ProductRow)(this.NewRow()));

   rowProductRow.ItemArray = new object[]

   {

      null,

      ProductName,

      SupplierID,

      CategoryID,

      QuantityPerUnit,

      UnitPrice,

      UnitsInStock,

      UnitsOnOrder,

      ReorderLevel,

      Discontinued

   };

   this.Rows.Add(rowProductRow);

   return rowProductRow;

}

DataSet派生类中的InitClass成员把表添加到DataSet,与此相同,ProductDataTable上的InitClass成员把列添加到DataTable中,每一列的属性都按需要设置,再把该列添加到列集合的尾部。

private void InitClass()

{

   this.columnProductID = new DataColumn ( "ProductID",

                                       typeof(int),

                                            null,

                                            System.Data.MappingType.Element);

   this.Columns.Add(this.columnProductID);

   // Other columns removed for clarity

 

   this.columnProductID.AutoIncrement = true;

   this.columnProductID.AllowDBNull = false;

   this.columnProductID.ReadOnly = true;

   this.columnProductName.AllowDBNull = false;

   this.columnDiscontinued.AllowDBNull = false;

}

 

public ProductRow NewProductRow()

{

    return ((ProductRow)(this.NewRow()));

}

NewRowFromBuilderDataTable NewRow()方法内部调用,创建了一个强类型化的新行。DataRowBuilder实例由DataTable创建,其成员仅能在System.Data程序集中访问。

protected override DataRow NewRowFromBuilder(DataRowBuilder builder)

{

   return new ProductRow(builder);

}

最后一个要讨论的类是ProductRow,它派生于DataRow。这个类用于提供对数据表中所有字段的类型安全的访问。它封装了特定行的存储器,并提供成员来读取(和写入)表中的每个字段。

另外,对于每个可为空的字段,还有函数可以把该字段设置为null,并检查该字段是否为null。下面的示例列出了SupplierID列的函数:

[System.Diagnostics.DebuggerStepThrough()]

public class ProductRow : DataRow

{

   private ProductDataTable tableProduct;

   

   internal ProductRow(DataRowBuilder rb) : base(rb)

   {

      this.tableProduct = ((ProductDataTable)(this.Table));

   }

   

   public int ProductID

   {

      get { return ((int)(this[this.tableProduct.ProductIDColumn])); }

      set { this[this.tableProduct.ProductIDColumn] = value; }

   }

  // Other column accessors/mutators removed for clarity

 

   public bool IsSupplierIDNull()

   {

      return this.IsNull(this.tableProduct.SupplierIDColumn);

   }

   

   public void SetSupplierIDNull()

   {

      this[this.tableProduct.SupplierIDColumn] = System.Convert.DBNull;

   }

}

下面的代码利用XSD工具中的类的输出从Product表中检索数据,并在控制台上显示这些数据:

using System;

using System.Data;

using System.Data.SqlClient;

 

public class XSD_DataSet

{

   public static void Main()

   {

      string source = "server=(local)\\NetSDK;" +

                   "uid=QSUser;pwd=QSPassword;" +

                   "database=northwind";

      string select = "SELECT * FROM Products";

      SqlConnection conn = new SqlConnection(source);

      SqlDataAdapter da = new SqlDataAdapter(select , conn);

      Products ds = new Products();

      da.Fill(ds , "Product");

      foreach(Products.ProductRow row in ds.Product )

      Console.WriteLine("'{0}' from {1}" ,

                      row.ProductID ,

                      row.ProductName);

   }

}

重要的代码已突出显示出来了。XSD文件的输出结果包含一个派生于DataSet的类Products,使用数据适配器来创建和填充这个类。foreach语句使用了强类型化的ProductRowProduct属性,返回Product数据表。

要编译这个示例,执行下面的命令:

xsd product.xsd /d

csc /recurse:*.cs

第一个命令从Products.XSD模式中生成Products.cs文件,然后,csc命令使用/recurse:*.cs参数查找扩展名为.cs的文件,并把它们添加到所生成的程序集中。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论