July 6, 2009, 9:13 a.m.
posted by pitbull
The XmlAdapter Class and XML ViewsThe XmlAdapter class provides query and update capabilities via an XML view to a SQL Server database. If you are a SQLXML advocate, you may be familiar with updategrams, which provide a declarative format for pushing changes to SQL Server. The XmlAdapter provides a programmatic way to achieve this in code, and it notably works with any XML document that is mapped. The XmlAdapter ClassThe XmlAdapter class provides the ability to synchronize changes made to an XPathDocument2 instance with SQL Server. Figure through 7.4 contain a concise reference to the constructors, properties, methods, and event of this class. Then we'll look at some of the associated classes before examining how we can use the XmlAdapter in code examples. The XmlUpdateEventArgs ClassThe XmlUpdateEventArgs class provides information about failures for updates to SQL Server, the items that were affected, and the underlying error that occurred during the update. Figure documents the properties of this class.
The XmlDataSourceResolver ClassThe XmlDataSourceResolver class provides the information about database connections and XML documents over which queries are performed. The XmlAdapter and XQueryProcessor classes use this to provide a set of named values at query execution time and to determine the data sources. The constructors, properties, and methods of this class are detailed in Figure, 7.7, and 7.8, respectively.
XML Views Using XSD, RSD, and MSD Schemas (Three-Part Mapping)To use the XmlAdapter, you need to understand how XML views are created for querying and updating SQL Server. In Chapter 5 we discussed how XML views are a declarative XML provider for translating from the relational domain to the XML domain, by mapping elements and attributes to tables and columns. Let's look at what it takes to write an XML view using the declarative mapping format. The mapping translates structure and relationships between data models, and in order to achieve this we use a persistent representation of each. To see it in more detail, we'll examine the mapping for just a small section of the Northwind sample database found in SQL Server 2000. We'll map the Customers, Orders, and related Products tables. Recall that the three-part mapping requires three documents:
The XSD View for the Northwind DatabaseThe XSD document (itself an XML schema) defines the shape of the XML view that is exposed and that is available to query against. In this example we create a structure that is a list of customers, their orders, and the products for each order. We have also decided to represent additional information as attributes, such as the customer name. The XSD view is shown in Listing 7.1. The XSD Mapping Document
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="Customers">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Customer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Order">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Product">
<xsd:complexType>
<xsd:attribute name="UnitQuantity" type="xsd:integer"/>
<xsd:attribute name="ProductName" type="xsd:string"/>
<xsd:attribute name="UnitPrice" type="xsd:string" />
<xsd:attribute name="QuantityPerUnit" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="CustomerID" type="xsd:string" />
<xsd:attribute name="orderid" type="xsd:string"/>
<xsd:attribute name="OrderDate" type="xsd:string"/>
<xsd:attribute name="ShipCity" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="CustomerID" type="xsd:string" />
<xsd:attribute name="Country" type="xsd:string" />
<xsd:attribute name="City" type="xsd:string" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
The RSD View for the Northwind DatabaseThe RSD document specifies the shape of the relational view that represents the section of the database to which we want to map the XML view. The RSD is a serialized representation of the Northwind database tables, and Listing 7.2 shows the Customers, Orders, Order Details, and Products tables and the named columns for each table. The RSD Mapping Document
<rsd:Database Owner="sa" Name="Northwind"
xmlns:rsd="http://schemas.microsoft.com/data/2002/09/28/rsd">
<rsd:Schema Name="dbo">
<rsd:Tables>
<rsd:Table Name="Orders">
<rsd:Columns>
<rsd:Column Name="OrderID" AutoIncrement="1" IncrementStep="1"
SqlType="int" IncrementSeed="1" />
<rsd:Column Name="CustomerID" SqlType="nchar" AllowDbNull="1"
Length="5" />
<rsd:Column Name="EmployeeID" SqlType="int" AllowDbNull="1" />
<rsd:Column Name="OrderDate" SqlType="datetime" AllowDbNull="1" />
<rsd:Column Name="RequiredDate" SqlType="datetime"
AllowDbNull="1"/>
<rsd:Column Name="ShippedDate" SqlType="datetime"
AllowDbNull="1"/>
<rsd:Column Name="ShipVia" SqlType="int" AllowDbNull="1" />
<rsd:Column Name="Freight" SqlType="money" AllowDbNull="1" />
<rsd:Column Name="ShipName" SqlType="nvarchar" AllowDbNull="1"
Length="40" />
<rsd:Column Name="ShipAddress" SqlType="nvarchar" AllowDbNull="1"
Length="60" />
<rsd:Column Name="ShipCity" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
<rsd:Column Name="ShipRegion" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
<rsd:Column Name="ShipPostalCode" SqlType="nvarchar"
AllowDbNull="1" Length="10" />
<rsd:Column Name="ShipCountry" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
</rsd:Columns>
<rsd:Constraints>
<rsd:PrimaryKey Name="PK_Orders">
<rsd:ColumnRef Name="OrderID" />
</rsd:PrimaryKey>
<rsd:ForeignKey Name="FK_Orders_Customers"
ForeignTable="Customers"
UpdateRule="None" DeleteRule="None">
<rsd:ColumnMatch Name="CustomerID" ForeignName="CustomerID" />
</rsd:ForeignKey>
</rsd:Constraints>
</rsd:Table>
<rsd:Table Name="Products">
<rsd:Columns>
<rsd:Column Name="ProductID" AutoIncrement="1" IncrementStep="1"
SqlType="int" IncrementSeed="1" />
<rsd:Column Name="ProductName" SqlType="nvarchar" Length="40" />
<rsd:Column Name="SupplierID" SqlType="int" AllowDbNull="1" />
<rsd:Column Name="CategoryID" SqlType="int" AllowDbNull="1" />
<rsd:Column Name="QuantityPerUnit" SqlType="nvarchar"
AllowDbNull="1" Length="20" />
<rsd:Column Name="UnitPrice" SqlType="money" AllowDbNull="1" />
<rsd:Column Name="UnitsInStock" SqlType="smallint"
AllowDbNull="1"/>
<rsd:Column Name="UnitsOnOrder" SqlType="smallint"
AllowDbNull="1"/>
<rsd:Column Name="ReorderLevel" SqlType="smallint"
AllowDbNull="1"/>
<rsd:Column Name="Discontinued" SqlType="bit" />
</rsd:Columns>
<rsd:Constraints>
<rsd:PrimaryKey Name="PK_Products">
<rsd:ColumnRef Name="ProductID" />
</rsd:PrimaryKey>
</rsd:Constraints>
</rsd:Table>
<rsd:Table Name="Order Details">
<rsd:Columns>
<rsd:Column Name="OrderID" SqlType="int" />
<rsd:Column Name="ProductID" SqlType="int" />
<rsd:Column Name="UnitPrice" SqlType="money" />
<rsd:Column Name="Quantity" SqlType="smallint" />
<rsd:Column Name="Discount" SqlType="real" />
</rsd:Columns>
<rsd:Constraints>
<rsd:PrimaryKey Name="PK_Order_Details">
<rsd:ColumnRef Name="OrderID" />
<rsd:ColumnRef Name="ProductID" />
</rsd:PrimaryKey>
<rsd:ForeignKey Name="FK_Order_Details_Orders"
ForeignTable="Orders" UpdateRule="None" DeleteRule="None">
<rsd:ColumnMatch Name="OrderID" ForeignName="OrderID" />
</rsd:ForeignKey>
<rsd:ForeignKey Name="FK_Order_Details_Products"
ForeignTable="Products" UpdateRule="None" DeleteRule="None">
<rsd:ColumnMatch Name="ProductID" ForeignName="ProductID" />
</rsd:ForeignKey>
</rsd:Constraints>
</rsd:Table>
<rsd:Table Name="Customers">
<rsd:Columns>
<rsd:Column Name="CustomerID" SqlType="nchar" Length="5" />
<rsd:Column Name="CompanyName" SqlType="nvarchar" Length="40" />
<rsd:Column Name="ContactName" SqlType="nvarchar" AllowDbNull="1"
Length="30" />
<rsd:Column Name="ContactTitle" SqlType="nvarchar"
AllowDbNull="1"
Length="30" />
<rsd:Column Name="Address" SqlType="nvarchar" AllowDbNull="1"
Length="60" />
<rsd:Column Name="City" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
<rsd:Column Name="Region" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
<rsd:Column Name="PostalCode" SqlType="nvarchar" AllowDbNull="1"
Length="10" />
<rsd:Column Name="Country" SqlType="nvarchar" AllowDbNull="1"
Length="15" />
<rsd:Column Name="Phone" SqlType="nvarchar" AllowDbNull="1"
Length="24" />
<rsd:Column Name="Fax" SqlType="nvarchar" AllowDbNull="1"
Length="24" />
</rsd:Columns>
<rsd:Constraints>
<rsd:PrimaryKey Name="PK_Customers">
<rsd:ColumnRef Name="CustomerID" />
</rsd:PrimaryKey>
</rsd:Constraints>
</rsd:Table>
</rsd:Tables>
</rsd:Schema>
</rsd:Database>
The MSD Mapping Transformation DocumentThe MSD file ties together the XSD and the RSD views by mapping structure and relationships between the XML and relational domains. The MSD mapping for our example is shown in Listing 7.3. The MSD Mapping Document
<m:MappingSchema
xmlns:m="http://schemas.microsoft.com/data/2002/09/28/mapping">
<m:DataSources>
<m:DataSource Name="XML" Direction="Target" Type="Xml">
<m:Schema Location="nwind.xsd" />
</m:DataSource>
<m:DataSource Name="Northwind" Direction="Source" Type="SQL Server">
<m:Schema Location="nwind.rsd" />
<m:Variable Name="Orders" Select="Orders" />
<m:Variable Name="Customers" Select="Customers"/>
<m:Variable Name="Products" Select="Products"/>
<m:Variable Name="OrderDetails" Select="Order Details"/>
<m:Relationship Name="rel1" FromVariable="Customers"
ToVariable="Orders">
<m:FieldJoin From="CustomerID" To="CustomerID" />
</m:Relationship>
<m:Relationship Name="rel2" FromVariable="Orders"
ToVariable="OrderDetails">
<m:FieldJoin From="OrderID" To="OrderID" />
</m:Relationship>
<m:Relationship Name="rel3" FromVariable="OrderDetails"
ToVariable="Products">
<m:FieldJoin From="ProductID" To="ProductID" />
</m:Relationship>
</m:DataSource>
</m:DataSources>
<m:Mappings>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer">
<m:FieldMap SourceField="CompanyName" TargetField="@name" />
<m:FieldMap SourceField="CustomerID" TargetField="@CustomerID" />
<m:FieldMap SourceField="Country" TargetField="@Country" />
<m:FieldMap SourceField="City" TargetField="@City" />
</m:Map
<m:Map SourceVariable="Orders"
TargetSelect="/Customers/Customer/Order">
<m:FieldMap SourceField="orderid" TargetField="@orderid" />
<m:FieldMap SourceField="CustomerID" TargetField="@CustomerID" />
<m:FieldMap SourceField="OrderDate" TargetField="@OrderDate" />
<m:FieldMap SourceField="ShipCity" TargetField="@ShipCity" />
</m:Map>
</m:Mappings>
</m:MappingSchema>
Breaking Down the MSD MappingTo help you understand the nature of these mapping documents, we'll break down the two top-level element definitions in the MSD (the DataSources element and Mappings element) and examine their child elements. The DataSources ElementThe <DataSources> element contains data source references and declarations, as described below.
The Mappings ElementThe <Mappings> element maps between the XSD and RSD schemas and is a container for the Map elements. It contains the following element definitions.
In order to map this element-centric XSD, the MSD mapping would need to be amended as shown in Listing 7.4. The Amended MSD for Element-Centric Mapping
<m:Mappings>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer"/>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer/name">
<m:FieldMap SourceField="name" TargetField="text()"/>
</m:Map>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer/CustomerID">
<m:FieldMap SourceField="CustomerID" TargetField="text()"/>
</m:Map>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer/Country">
<m:FieldMap SourceField="Country" TargetField="text()"/>
</m:Map>
<m:Map SourceVariable="Customers"
TargetSelect="/Customers/Customer/City">
<m:FieldMap SourceField="City" TargetField="text()"/>
</m:Map>
</m:Mappings>
Note that the RSD does not have to change, only the structure of the XML view and how it is mapped. The XmlViewSchema and XmlViewSchemaDictionary ClassesNow that we have shown the mapping format and syntax, there are two classes we should introduce that represent the XML view: the XmlView Schema and the XmlViewSchemaDictionary. Both of these can be found in the System.Xml.Query namespace of the Framework. The XmlViewSchema class represents a compiled XML view. The mapping allows a non-XML source, such as a relational database, to represent its data in a shape that conforms to an XML schema or XSD. In mapping terminology, the exposed XSD is the "target" or "query" side of the mapping while the underlying data source is the "source" or "persistent" side of the mapping. The XmlViewSchemaDictionary is a dictionary of XmlViewSchema objects, where the five overloads of the Add method (see Figure earlier) provide the ability to add new XML views. The views can be used to issue queries and updates to the data source such as SQL Server. You pass in an MSD file reference, and the Add method creates a new XmlViewSchema object, compiles the mapping, and adds this to the dictionary. The System.Data.Mapping NamespaceThe System.Data.Mapping namespace contains object models for the MSD and RSD formats. The one class that is most important here for using an XmlAdapter is the MappingSchema, which represents the compiled state for the MSD file. This is used when we call the Update method on the XmlAdapter. Meanwhile, the XSD object model classes are in the System.Xml.Schema namespace and were shipped in version 1.x of the .NET Framework. Many classes in this namespace are beyond the scope of this book and for the most part are never needed by applications using XML views—only by tools that manipulate the object models. |
- Comment