轻松玩转Typed DataSet, Part II
轻松玩转Typed DataSet, Part II
Written by: Rickie Lee
Dec. 08, 2004
本文继续前面《轻松玩转Typed DataSet, Part I》,这里演练如何通过批注(Annotations)来定制类型化 DataSet,并调用定制的Typed DataSet。
三、通过批注(Annotations)来定制类型化 DataSet
批注使您能够在不修改基础架构的情况下修改类型化 DataSet 中元素的名称。如果修改基础架构中元素的名称,则会使类型化 DataSet 引用不存在于数据源中的对象,并且会丢失对存在于数据源中的对象的引用。
利用批注,您可以使用更有意义的名称来自定义类型化 DataSet 中对象的名称,从而使代码更易于阅读,类型化 DataSet 更易于为客户端使用,同时保持基础架构不变。
将VS.Net IDE默认的DataSet窗口切换到XML窗口。
1. 默认创建的VS.Net IDE自动生成的Orders table的架构元素
<xs:element name="Orders">
<xs:complexType>
<xs:sequence>
<xs:element name="OrderID" msdata:ReadOnly="true" msdata:AutoIncrement="true" type="xs:int" />
<xs:element name="CustomerID" type="xs:string" minOccurs="0" />
<xs:element name="EmployeeID" type="xs:int" minOccurs="0" />
<xs:element name="OrderDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="RequiredDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="ShippedDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="ShipVia" type="xs:int" minOccurs="0" />
<xs:element name="Freight" type="xs:decimal" minOccurs="0" />
<xs:element name="ShipName" type="xs:string" minOccurs="0" />
<xs:element name="ShipAddress" type="xs:string" minOccurs="0" />
<xs:element name="ShipCity" type="xs:string" minOccurs="0" />
<xs:element name="ShipRegion" type="xs:string" minOccurs="0" />
<xs:element name="ShipPostalCode" type="xs:string" minOccurs="0" />
<xs:element name="ShipCountry" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
上述XML Schema会生成OrdersRow这一对象名称OrderDataSet.OrdersRow,还有一个名为Orders的DataRowCollection Class(OrderDataSet.Orders)。
通过批注架构并标识 DataRow 和 DataRowCollection 对象的新名称,使上述对象名称更有意义。
首先需要在schema header中增加namespace引用,允许定制元素属性,如下所示:
<xs:schema id="AnnotationTypedDataset" targetNamespace="http://tempuri.org/AnnotationTypedDataset.xsd"
elementFormDefault="qualified" attributeFormDefault="qualified" xmlns="http://tempuri.org/AnnotationTypedDataset.xsd"
xmlns:mstns="http://tempuri.org/AnnotationTypedDataset.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:codegen="urn:schemas-microsoft-com:xml-msprop">
下面是上一架构的批注版本:
<xs:element name="Orders" codegen:typedName="Order" codegen:typedPlural="Orders">
<xs:complexType>
<xs:sequence>
<xs:element name="OrderID" msdata:ReadOnly="true" msdata:AutoIncrement="true" type="xs:int" />
<xs:element name="CustomerID" type="xs:string" minOccurs="0" />
<xs:element name="EmployeeID" type="xs:int" minOccurs="0" />
<xs:element name="OrderDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="RequiredDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="ShippedDate" type="xs:dateTime" minOccurs="0" />
<xs:element name="ShipVia" type="xs:int" minOccurs="0" />
<xs:element name="Freight" type="xs:decimal" minOccurs="0" />
<xs:element name="ShipName" type="xs:string" minOccurs="0" />
<xs:element name="ShipAddress" type="xs:string" minOccurs="0" />
<xs:element name="ShipCity" type="xs:string" minOccurs="0" />
<xs:element name="ShipRegion" type="xs:string" minOccurs="0" />
<xs:element name="ShipPostalCode" type="xs:string" minOccurs="0" />
<xs:element name="ShipCountry" type="xs:string" minOccurs="0" />
</xs:sequence>
</xs:complexType>
</xs:element>
将 Order的值指定为 typedName 将生成 DataRow 对象名称 Order。将 Orders 的值指定为 typedPlural 则会保留 DataRowCollection 名称 Orders。
下表显示可用的批注(From MSDN):
批注 | 说明 |
typedName | 对象的名称。 |
typedPlural | 对象集合的名称。 |
typedParent | 对象在父关系中被引用时的名称。 |
typedChildren | 用于从子关系中返回对象的方法的名称。 |
nullValue | 如果基础值为 DBNull,则为值。有关 nullValue 批注的信息,请参见下表。默认为 _throw。 |
下表显示可为 nullValue 批注指定的值(FROM MSDN):
nullValue | 说明 |
替换值 | 指定要返回的值。所返回的值必须匹配该元素的类型。例如,使用 nullValue="0" 可为空整数字段返回 0。 |
_throw | 引发异常。这是默认值。 |
_null | 如果遇到基元类型,则返回空引用或引发异常。 |
_empty | 对于字符串返回 String.Empty;否则,返回从空构造函数创建的对象。如果遇到基元类型,则引发异常。 |
下面是OrderDetails架构的批注版本:
<xs:element name="OrderDetails" codegen:typedName="OrderDetail" codegen:typedPlural="OrderDetails">
<xs:complexType>
<xs:sequence>
<xs:element name="OrderID" type="xs:int" />
<xs:element name="ProductID" type="xs:int" />
<xs:element name="UnitPrice" type="xs:decimal" />
<xs:element name="Quantity" type="xs:short" />
<xs:element name="Discount" type="xs:float" />
</xs:sequence>
</xs:complexType>
</xs:element>
通过typedParent和typedChildren属性更改Order/OrderDetails对象的引用方法:
<xs:keyref name="OrdertoOrderDetails" refer="AnnotationTypedDatasetKey1" codegen:typedParent="Order" codegen:typedChildren="GetOrderDetails">
<xs:selector xpath=".//mstns:OrderDetails" />
<xs:field xpath="mstns:OrderID" />
</xs:keyref>
这样Order对象就产生一个GetOrderDetails()方法,沿着Relationship向下导航到OrderDetail对象,OrderDetails对象生成一个Order()方法,沿着Relationship向上导航到Order对象。
四、调用定制的Typed DataSet
如下是调用上述通过Annotation定制的Typed DataSet,Code snippet如下所示:
AnnotationTypedDataset theOrderDS = new AnnotationTypedDataset();
string strSelectOrders = "Select * From Orders ";
strSelectOrders += "Select * From [Order Details]";
SqlHelper.FillDataset(connStr, CommandType.Text, strSelectOrders, theOrderDS, new string[] {"Orders", "OrderDetails"});
StringBuilder strResults = new StringBuilder();
foreach(AnnotationTypedDataset.Order theOrder in theOrderDS.Orders)
{
strResults.Append(theOrder.OrderID.ToString() + " "
+ theOrder.CustomerID.ToString() + " "
+ theOrder.EmployeeID.ToString() + Environment.NewLine);
strResults.Append("Order Details: ");
strResults.Append(theOrder.GetChildRows("OrdertoOrderDetails").Length.ToString() + " ");
strResults.Append(theOrder.GetOrderDetails().Length.ToString());
strResults.Append(Environment.NewLine);
}
txtResults.Text = strResults.ToString();
代码比较简单,上述代码调用了SqlHelper Class(Microsoft Data Access Application Block)的FillDataset方法,来完成DataSet的填充。与调用默认的Typed DataSet代码相比较有细微差别。
Any questions or errors, please leave comments below. Thanks.
References:
1. MSDN
2. Rickie, 轻松玩转Typed DataSet, Part I