在允许通过弱类型变量对值进行晚期绑定访问的同时,DataSet 还允许通过强类型比喻对数据进行访问。使用用户友好名称和强类型变量,可以访问作为 DataSet 一部分的表和列。

类型化的 DataSet 是从 DataSet 派生的类。同样,它继承 DataSet 的所有方法、事件和属性。此外,类型化的 DataSet 提供强类型的方法、事件和属性。这意味着可以按名称(而不是使用基于集合的方法)访问表和列。除了提高代码的可读性之外,类型化的 DataSet 还允许 Visual Studio .NET 代码编辑器自动填写您键入的行。

此外,强类型的 DataSet 还允许在编译时对作为正确类型的值进行访问。通过强类型的 DataSet,将在编译代码时(而不是在运行时)捕获类型不匹配错误。

本节内容

生成强类型 DataSet:描述如何创建和使用强类型 DataSet

给定一个符合 XML 架构定义语言 XSD 标准的 XML 架构,则可使用 .NET 框架 SDK 附带的 XSD.exe 工具来生成强类型 DataSet

以下代码显示使用该工具生成 DataSet 的语法。

xsd.exe /d /l:C# XSDSchemaFileName.xsd /n:XSDSchema.Namespace

在此语法中,/d 指令指示该工具生成 DataSet/l: 告诉该工具要使用哪种语言(例如 C# 或 Visual Basic .NET)。可选的 /n: 指令指示该工具另外为 DataSet 生成名为 XSDSchema.Namespace 的命名空间。该命令的输出为 XSDSchemaFileName.cs,它可以在 ADO.NET 应用程序中编译和使用。所生成的代码可以编译成库或模块。

以下代码显示使用 C# 编译器 (csc.exe) 将生成的代码编译成库的语法。

csc.exe /t:library XSDSchemaFileName.cs /r:System.dll /r:System.Data.dll

/t: 指令指示该工具编译成库,/r: 指令指定进行编译所需的依赖库。该命令的输出为 XSDSchemaFileName.dll,它可以在使用 /r: 指令编译 ADO.NET 应用程序时传递到编译器。

以下代码显示访问向 ADO.NET 应用程序中的 XSD.exe 传递的命名空间的语法。

Imports XSDSchema.Namespace

using XSDSchema.Namespace;

以下代码示例使用名为 CustomerDataSet 的类型化 DataSet 来加载 Northwind 数据库中客户的列表。当使用 Fill 方法加载数据后,该示例会使用类型化 CustomersRow (DataRow) 对象循环通过 Customers 表中的每个客户。它提供了对 CustomerID 列的直接访问,而不是通过 DataColumnCollection 来访问该列。

Dim custDS As CustomerDataSet= New CustomerDataSet()
Dim custCMD As SqlDataAdapter New SqlDataAdapter("SELECT * FROM Customers",
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

custCMD.Fill(custDS, "Customers")

Dim custRow As CustomerDataSet.CustomersRow
For Each custRow In custDS.Customers
  Console.WriteLine(custRow.CustomerID)
Next

CustomerDataSet custDS = new CustomerDataSet();
SqlDataAdapter custCMD = new SqlDataAdapter("SELECT * FROM Customers",
"Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind");

custCMD.Fill(custDS, "Customers");

foreach(CustomerDataSet.CustomersRow custRow in custDS.Customers)
  Console.WriteLine(custRow.CustomerID);

下面是用于该示例的 XML 架构。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="CustomerDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="CustomerDataSet" msdata:IsDataSet="true">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Customers">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

将批注用于类型化DataSet:描述如何批注 XML 架构定义语言 (XSD) 架构,该架构用于生成强类型 DataSet,以便在不更改基础架构的情况下提供 DataSet 元素友好的名称。
批注使您能够在不修改基础架构的情况下修改类型化 DataSet 中元素的名称。如果修改基础架构中元素的名称,则会使类型化 DataSet 引用不存在于数据源中的对象,并且会丢失对存在于数据源中的对象的引用。

利用批注,您可以使用更有意义的名称来自定义类型化 DataSet 中对象的名称,从而使代码更易于阅读,类型化 DataSet 更易于为客户端使用,同时保持基础架构不变。例如,Northwind 数据库中 Customers 表的以下架构元素会生成 CustomersRow 这一 DataRow 对象名称和一个名为 CustomersDataRowCollection

<xs:element name="Customers">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

DataRowCollection 名称 Customers 在客户端代码中是有意义的,但 DataRow 名称 CustomersRow 则会导致误解,因为它是单个对象。此外,在通常情况下,将不使用 Row 标识符来引用该对象,而仅将该对象当作 Customer 对象来引用。解决方案是批注架构并标识 DataRowDataRowCollection 对象的新名称。下面是上一架构的批注版本。

<xs:element name="Customers" codegen:typedName="Customer" typedPlural="Customers">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="CustomerID" type="xs:string" minOccurs="0" />
    </xs:sequence>
  </xs:complexType>
</xs:element>

Customer 的值指定为 typedName 将生成 DataRow 对象名称 Customer。将 Customers 的值指定为 typedPlural 则会保留 DataRowCollection 名称 Customers

下表显示可用的批注。

批注 说明
typedName 对象的名称。
typedPlural 对象集合的名称。
typedParent 对象在父关系中被引用时的名称。
typedChildren 用于从子关系中返回对象的方法的名称。
nullValue 如果基础值为 DBNull,则为值。有关 nullValue 批注的信息,请参阅下表。默认为 _throw

下表显示可为 nullValue 批注指定的值。

nullValue 说明
替换值 指定要返回的值。所返回的值必须匹配该元素的类型。例如,使用 nullValue="0" 可为空整数字段返回 0。
_throw 引发异常。这是默认值。
_null 如果遇到基元类型,则返回空引用或引发异常。
_empty 对于字符串返回 String.Empty;否则,返回从空构造函数创建的对象。如果遇到基元类型,则引发异常。

下表显示类型化 DataSet 中对象的默认值以及可用的批注。

对象/方法/事件 默认值 批注
DataTable TableNameDataTable typedPlural
DataTable 方法 NewTableNameRow

AddTableNameRow

DeleteTableNameRow

typedName
DataRowCollection TableName typedPlural
DataRow TableNameRow typedName
DataColumn DataTable.ColumnNameColumn

DataRow.ColumnName

typedName
Property PropertyName typedName
Child Accessor GetChildTableNameRows typedChildren
Parent Accessor TableNameRow typedParent
DataSet 事件 TableNameRowChangeEvent

TableNameRowChangeEventHandler

typedName

若要使用类型化 DataSet 批注,则必须在 XML 架构定义语言 (XSD) 架构中包含以下 xmlns 引用。

xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"

下面是一个批注架构示例,它公开 Northwind 数据库的 Customers 表并包含与 Orders 表的关系。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="CustomerDataSet" 
      xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"
      xmlns="" 
      xmlns:xs="http://www.w3.org/2001/XMLSchema" 
      xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="CustomerDataSet" msdata:IsDataSet="true">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Customers" codegen:typedName="Customer" codegen:typedPlural="Customers">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="CustomerID" codegen:typedName="CustomerID" 
type="xs:string" minOccurs="0" />
              <xs:element name="CompanyName" codegen:typedName="CompanyName" 
type="xs:string" minOccurs="0" />
              <xs:element name="Phone" codegen:typedName="Phone" 
codegen:nullValue=""
type="xs:string" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
        <xs:element name="Orders" codegen:typedName="Order" codegen:typedPlural="Orders">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="OrderID" codegen:typedName="OrderID" type="xs:int" minOccurs="0" />
              <xs:element name="CustomerID" codegen:typedName="CustomerID" 
codegen:nullValue=""
type="xs:string" minOccurs="0" />
              <xs:element name="EmployeeID" codegen:typedName="EmployeeID" 
codegen:nullValue="0"
type="xs:int" minOccurs="0" />
              <xs:element name="OrderDate" codegen:typedName="OrderDate" 
codegen:nullValue="1980-01-01T00:00:00"
type="xs:dateTime" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
    <xs:unique name="Constraint1">
      <xs:selector xpath=".//Customers" />
      <xs:field xpath="CustomerID" />
    </xs:unique>
    <xs:keyref name="CustOrders" refer="Constraint1" codegen:typedParent=
"Customer" codegen:typedChildren="GetOrders"
>
      <xs:selector xpath=".//Orders" />
      <xs:field xpath="CustomerID" />
    </xs:keyref>
  </xs:element>
</xs:schema>

以下代码示例使用从示例架构创建的强类型 DataSet。它使用一个 DataAdapter 填充 Customers 表,并使用另一个 DataAdapter 填充 Orders 表。强类型 DataSet 定义 DataRelations

Dim nwindConn As SqlConnection = New SqlConnection("Data Source=
localhost;Integrated Security=SSPI;Initial Catalog=northwind")
Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT CustomerID,
 CompanyName, Phone FROM Customers", nwindConn)
Dim orderDA As SqlDataAdapter = New SqlDataAdapter("SELECT OrderID,
CustomerID, EmployeeID, OrderDate FROM Orders",nwindConn)

' Populate a strongly typed DataSet.
nwindConn.Open()
Dim custDS As CustomerDataSet = New CustomerDataSet()
custDA.Fill(custDS, "Customers")
orderDA.Fill(custDS, "Orders")
nwindConn.Close()

' Add a strongly typed event.
AddHandler custDS.Customers.CustomerChanged, &
           New CustomerDataSet.CustomerChangeEventHandler(AddressOf OnCustomerChanged)

' Add a strongly typed DataRow.
Dim newCust As CustomerDataSet.Customer = custDS.Customers.NewCustomer()
newCust.CustomerID = "NEW01"
newCust.CompanyName = "My New Company"
custDS.Customers.AddCustomer(newCust)

' Navigate the child relation.
Dim customer As CustomerDataSet.Customer
Dim order As CustomerDataSet.Order

For Each customer In custDS.Customers
  Console.WriteLine(customer.CustomerID)
  For Each order In customer.GetOrders()
    Console.WriteLine(vbTab & order.OrderID)
  Next
Next

Private Shared Sub OnCustomerChanged(sender As Object, e As CustomerDataSet.CustomerChangeEvent)

End Sub

SqlConnection nwindConn = new SqlConnection("Data Source=
localhost;Integrated Security=SSPI;Initial Catalog=northwind");
SqlDataAdapter custDA = new SqlDataAdapter("SELECT CustomerID,
 CompanyName, Phone FROM Customers", nwindConn);
SqlDataAdapter orderDA = new SqlDataAdapter("SELECT OrderID,
CustomerID, EmployeeID, OrderDate FROM Orders", nwindConn);

// Populate a strongly typed DataSet.
nwindConn.Open();
CustomerDataSet custDS = new CustomerDataSet();
custDA.Fill(custDS, "Customers");
orderDA.Fill(custDS, "Orders");
nwindConn.Close();

// Add a strongly typed event.
custDS.Customers.CustomerChanged += new
  CustomerDataSet.CustomerChangeEventHandler(OnCustomerChanged);

// Add a strongly typed DataRow.
CustomerDataSet.Customer newCust = custDS.Customers.NewCustomer();
newCust.CustomerID = "NEW01";
newCust.CompanyName = "My New Company";
custDS.Customers.AddCustomer(newCust);

// Navigate the child relation.
foreach(CustomerDataSet.Customer customer in custDS.Customers)
{
  Console.WriteLine(customer.CustomerID);
  foreach(CustomerDataSet.Order order in customer.GetOrders())
    Console.WriteLine("\t" + order.OrderID);
}

protected static void OnCustomerChanged(object sender, CustomerDataSet.CustomerChangeEvent e)
{

}