Tải bản đầy đủ (.pdf) (60 trang)

Professional ASP.NET 2.0 XML phần 5 potx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.37 MB, 60 trang )

Let’s start by discussing the DataSet object and the XML support it offers.
ADO.NET and XML
One of the key objects in ADO.NET is the DataSet. A System.Data.DataSet object stores data in a hierar-
chical object model and hence is similar to a relational database in structure. Besides storing data in a discon-
nected cache, a
DataSet also stores information such as the constraints and relationships that are defined
for the
DataSet. You use a DataSet to access data from the tables when disconnected from the data source.
You can access the ADO.NET DataSet objects in two pathways regardless of the data source: the DataSet
properties, methods, and events; and the XML DOM through the use of
XmlDataDocument object. Both of
these techniques have parallel access methods that permit you to follow sequential or hierarchical paths
through your data.
ADO.NET supports the ability to construct
DataSet objects from either XML streams or documents.
These XML sources can include data or schema, or both. The schema is expressed as Extensible Schema
Definition language (XSD) — another form of XML. You can also export data from a DataSet to an XML
document, with or without the schema. This is handy when you have to send data through a firewall; in
most situations, a firewall won’t permit you to pass binary data.
Now that you have an understanding of the
DataSet, the next section goes into more detail on how to
populate a
DataSet with XML data from an XML document and how to save the contents of a DataSet
as XML.
Loading XML into a DataSet
There are several ways in which you can populate a DataSet with XML, but the most common is probably
to use one of the eight different versions of the
DataSet.ReadXml() method. Here are the first four.

ReadXml(Stream): This loads the DataSet with the XML in the stream object — that is, any
object that inherits from


System.IO.Stream, such as System.IO.FileStream; however, it
could just as easily be a stream of data coming down from a Web site, and so on.

ReadXml(String): This loads the DataSet with the XML stored in the file whose name you
provide.

ReadXml(TextReader): This loads the DataSet with the XML processed by the given text
reader—that is, any object that inherits from
System.IO.TextReader.

ReadXml(XmlReader): This loads the DataSet with the XML processed by the given XML
reader. As you’ve seen, the
XmlValidatingReader class inherits from
System.Xml.XmlReader, so you can pass an XmlValidatingReader to this function.
A DataSet can either be a typed DataSet or an untyped DataSet. A typed DataSet
is a class derived from a DataSet class and has an associated XML Schema. On the
other hand, an untyped
DataSet does not have an associated XML Schema. You see
more on typed
DataSets later in this chapter.
214
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 214
The other four ReadXml() overloads correspond to the previous four, but with an additional parameter
of type
XmlReadMode, which is the focus in the next section.
XmlReadMode
The System.Data.XmlReadMode enumeration is used to determine the behavior of the XML parser when
loading documents from various sources. Table 8-1 shows the members of the
XmlReadMode enumeration,

and the impact they have on the
DataSet and how it loads the XML.
Table 8-1. Members of XmlReadMode Enumeration
Member Description
Auto This is the default. It attempts to select one of the other previous
options automatically. If the data being loaded is a
DiffGram,
the
XmlReadMode is set to DiffGram. If the DataSet has
already been given a schema by some means, or the XML
document has an inline schema defined, the
XmlReadMode is set
to
ReadSchema. If the DataSet doesn’t contain a schema, there
is no inline schema defined and the XML document is not a
DiffGram, the XmlReadMode is set to InferSchema. Because of
this indirection, the
XmlReadMode.Auto may be slower than
using an explicit mode.
ReadSchema This option loads any inline schema supplied by the DataSet
and then load the data. If any schema information exists in the
DataSet prior to this operation, the schema can be extended
by the inline XML schema. If new table definitions exist in the
inline schema that already exists in the
DataSet, however, an
exception will be thrown.
IgnoreSchema Ignores any inline schema and loads the data into the existing
DataSet schema. Any data that does not match the schema is
discarded. If you are bulk loading data from existing XML
sources, it might be useful to enable this option to get better

performance.
InferSchema This option forces the DataSet to infer the schema from the
XML document, ignoring any inline schema in the document,
and extending any schema already in place in the
DataSet.
DiffGram An XML representation of a “before” and “after” state for data.
If you specify this argument, the
DataSet loads a DiffGram
and applies the changes it indicates to the DataSet.
Fragment This option reads XML fragments like an XML document with-
out a single root element. An example of this is the XML output
generated by FOR XML queries. In this option, the default
namespace is read as the inline schema.
215
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 215
Now that you have some information on the ReadXml() method and how to process XML, load some
XML data into a
DataSet. Before looking at the code, it is useful to examine the XML file (named
Products.xml) to be used. It is as follows:
<?xml version=”1.0” standalone=”yes”?>
<DocumentElement>
<Products>
<ProductID>1</ProductID>
<ProductName>Chai</ProductName>
<SupplierID>1</SupplierID>
<CategoryID>1</CategoryID>
<QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit>
<UnitPrice>18.0000</UnitPrice>
<UnitsInStock>39</UnitsInStock>

<UnitsOnOrder>0</UnitsOnOrder>
<ReorderLevel>10</ReorderLevel>
<Discontinued>false</Discontinued>
</Products>


</DocumentElement>
You can download the complete code of Products.xml from the Wrox Web site along with the support
material for this book. In Listing 8-1, you load the Products.xml file into a
DataSet and then display that
information in a GridView control.
Listing 8-1: Reading XML into a DataSet Using ReadXml
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Configuration”%>
<%@ Import Namespace=”System.Data”%>
<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{
DataSet productsDataSet;
string filePath = Server.MapPath(“App_Data/Products.xml”);
productsDataSet = new DataSet();
//Read the contents of the XML file into the DataSet
productsDataSet.ReadXml(filePath);
gridProducts.DataSource = productsDataSet.Tables[0].DefaultView;
gridProducts.DataBind();
}
</script>
<html xmlns=” >
<head runat=”server”>
<title>Reading XML Data into a DataSet object </title>

</head>
Depending on how much decision making needs to take place, using the default
Auto mode may perform more slowly than explicitly setting the read mode. A good
rule of thumb is to supply the read mode explicitly whenever you know what it will
be ahead of time.
216
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 216
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView id=”gridProducts” runat=”server”
AutoGenerateColumns=”False” CellPadding=”4”
HeaderStyle-BackColor=”blue” HeaderStyle-ForeColor=”White”
HeaderStyle-HorizontalAlign=”Center” HeaderStyle-Font-Bold=”True”>
<Columns>
<asp:BoundField HeaderText=”Product ID” DataField=”ProductID” />
<asp:BoundField HeaderText=”Price”
DataField=”UnitPrice” ItemStyle-HorizontalAlign=”Right” />
<asp:BoundField HeaderText=”Name” DataField=”ProductName” />
<asp:BoundField HeaderText=”Description”
DataField=”QuantityPerUnit” />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
In Listing 8-1, you load an XML file into a DataSet using the ReadXml() method and then simply bind
the

DataSet onto a GridView control that renders the DataSet contents onto the browser. Navigate to
the page and you should see an output similar to Figure 8-1.
Figure 8-1
If you call
ReadXml() to load a very large file, you may encounter slow performance. To ensure best
performance for
ReadXml(), on a large file, call the DataTable.BeginLoadData() method for
each table in the
DataSet and then call ReadXml(). Finally, call DataTable.EndLoadData() for
each table in the
DataSet as shown in the following example.
217
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 217
foreach (DataTable t in ds.Tables)
t.BeginLoadData();
ds.ReadXml(“file.xml”);
foreach (DataTable t in ds.Tables)
t.EndLoadData();
The BeginLoadData() method turns off notifications, index maintenance, and constraints while
loading data. The
EndLoadData() method turns on notifications, index maintenance, and constraints
after loading data.
DataSet Schemas
In the previous section, you learned that you can load a DataSet with XML data by using the
ReadXml() method of the DataSet object. But what do you do when you want to load a DataSet
schema from an XML document?
You use either the
ReadXmlSchema() or the InferXmlSchema() method of the DataSet to load
DataSet schema information from an XML document. Before looking at these methods, it is important

to understand what schema inference is.
Schema Inference
Schema inference is a process that’s performed when a DataSet object without an existing data structure
attempts to load data from an XML document. The
DataSet will make an initial pass through the XML
document to infer the data structure and then a second pass to load the
DataSet with the information
contained in the document. There is a set of rules for inferring
DataSet schemas that is always followed.
Therefore, you can accurately predict what the schema inferred from a given XML document will look like.
Inference Rules
When inferring a schema from an XML document, a DataSet follows these rules.
❑ Elements with attributes become tables.
❑ Elements with child elements become tables.
❑ Repeating elements become columns in a single table.
❑ Attributes become columns.
❑ If the document (root) element has no attributes and no child elements that can be inferred to be
columns, it is inferred to be a
DataSet; otherwise, the document element becomes a table.
❑ For elements inferred to be tables that have no child elements and contain text, a new column
called Tablename_Text is created for the text of each of the elements. If an element with both
child nodes and text is inferred to be a table, the text is ignored.
❑ For elements that are inferred to be tables nested within other elements inferred to be tables, a
nested
DataRelation is created between the two tables.
218
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 218
Inference Rules in Action
Consider a sample XML document to understand what kind of schema the DataSet will infer from that

XML document.
<?xml version=”1.0” standalone=”yes”?>
<Products>
<Product>
<ProductID>1</ProductID>
<ProductName>Chai</ProductName>
</Product>
</Products>
If you load the XML into a DataSet object, you will notice that the DataSet automatically infers the
schema; then, if you write the schema of the
DataSet object to an XSD file using the WriteXmlSchema()
method, you will see the output shown in Listing 8-2.
Listing 8-2: XSD Schema Produced through Schema Inference
<?xml version=”1.0” standalone=”yes”?>
<xs:schema id=”Products” xmlns=”” xmlns:xs=” />xmlns:msdata=”urn:schemas-microsoft-com:xml-msdata”>
<xs:element name=”Products” msdata:IsDataSet=”true”
msdata:UseCurrentLocale=”true”>
<xs:complexType>
<xs:choice minOccurs=”0” maxOccurs=”unbounded”>
<xs:element name=”Product”>
<xs:complexType>
<xs:sequence>
<xs:element name=”ProductID” type=”xs:string” minOccurs=”0” />
<xs:element name=”ProductName” type=”xs:string” minOccurs=”0” />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:complexType>
</xs:element>

</xs:schema>
As you can see from the XSD schema output, the schema inference process has automatically inferred a
DataSet named Products, with a single table named Product. The most interesting aspect is that this
provides a schema inference tool that can be used in other contexts. For example, you can utilize the
schema inference tool to generate a schema that can be used to create a typed
DataSet later in the chapter.
Supplied Schemas
Instead of allowing the DataSet to infer the schema, you can supply a schema to a DataSet explicitly.
This is important because there are some limitations with the schema inference mechanism. For example,
schema inference can only go so far before it deviates from how you would really like the data to be
organized. It can’t infer data types, and it won’t infer existing column relationships — instead, it creates
new columns and new relationships.
219
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 219
Because of the inherent problems related to schema inference, there are times you might want to
explicitly supply a schema to your
DataSet object. There are several ways you can accomplish this.
Obviously, you can create one yourself by creating tables and columns in a
DataSet object and by using
the
WriteXmlSchema() method as described previously. Alternatively, you can supply an XSD file (or an
XmlSchema class) to the DataSet, or you can give the responsibility of generating the internal relational
structure to a
DataAdapter. When you supply a schema upfront, you can enforce constraints and richness
of programming through Intellisense. The next section starts by looking at the last of those options first.
The FillSchema Method
The DataAdapter has a FillSchema() method that executes a query on the database to fill the schema
information of table/tables into a
DataSet. For an example of a situation in which this facility might be

useful, imagine that you have an XML document whose data you would eventually like to add to a SQL
Server database. As a first step toward doing that, you simply fill the
DataSet with the schema of the
ContactType table and then load the XML document with the DataSet’s ReadXml() method. After
that, you can easily use the
DataSet to add to the SQL Server database. Before looking at the ASP.NET
page, examine the
ContactType.xml used for the purposes of this example.
<?xml version=”1.0” standalone=”yes”?>
<ContactType>
<Table>
<ContactTypeID>1</ContactTypeID>
<Name>Accounting Manager</Name>
<ModifiedDate>6/1/1998</ModifiedDate>
</Table>
<Table>
<ContactTypeID>2</ContactTypeID>
<Name>Assistant Sales Agent</Name>
<ModifiedDate>6/1/1998</ModifiedDate>
</Table>
</ContactType>
Now that you have looked at the XML file, Listing 8-3 shows the ASP.NET page in action.
Listing 8-3: Loading Schemas through FillSchema Method
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{

string filePath = Server.MapPath(“App_Data/ContactType.xml”);
string connString = WebConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select ContactTypeID, Name from Person.ContactType”;
DataSet contactsDataSet = new DataSet(“ContactType”);
using (SqlConnection sqlConn = new SqlConnection(connString))
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.FillSchema(contactsDataSet,SchemaType.Source);
}
220
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 220
contactsDataSet.ReadXml(filePath);
DataTable table = contactsDataSet.Tables[0];
int numCols = table.Columns.Count;
foreach (DataRow row in table.Rows)
{
for (int i = 0; i < numCols; i++)
{
Response.Write(table.Columns[i].ColumnName +
“ = “ + row[i].ToString() + “<br>”);
}
Response.Write(“<br>”);
}
}
</script>
<html xmlns=” >
<head runat=”server”>
<title>Loading XML Schema using FillSchema method</title>

</head>
<body>
<form id=”form1” runat=”server”>
<div>
</div>
</form>
</body>
</html>
For the code to work, you need to ensure that the web.config file has the <connectionStrings> element
present as follows:
<connectionStrings>
<add name=”adventureWorks” connectionString=”server=localhost;integrated
security=true;database=AdventureWorks;”/>
</connectionStrings>
After you have the connection string in the web.config file, you can then easily retrieve the connection
string from within the ASP.NET page.
string connString = ConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
After you have loaded the required schema from the ContactType table through the FillSchema()
method, you can then simply load the ContactType.xml document into the DataSet by calling the
ReadXml() method.
Similar to loading the
DataSet with the schema of the database table, you can also load a DataTable
with the schema of the database table and then load that DataTable with XML data from an external
XML document. This is made possible by the new XML support offered by the
DataTable.
The ReadXmlSchema Method
You use the ReadXmlSchema() method when you want to load only DataSet schema information (and
no data) from an XML document. This method loads a
DataSet schema using the XSD schema. The

ReadXmlSchema() method takes a stream, an XmlReader, or a file name as a parameter. In the event of
absence of an inline schema in the XML document, the
ReadXmlSchema() method interprets the schema
221
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 221
from the elements in the XML document. When you use the ReadXmlSchema() method to load a DataSet
that already contains a schema, the existing schema is extended, and new columns are added to the tables.
Any tables that do not exist in the existing schema are also added. Note the
ReadXmlSchema() method
throws an exception if the types of the column in the
DataSet and the column in the XML document are
incompatible.
The InferXmlSchema Method
You can also use the InferXmlSchema() method to load the DataSet schema from an XML document.
This method has the same functionality as that of the
ReadXml() method that uses the XmlReadMode
enumeration value set to InferSchema. The InferXmlSchema() method, besides enabling you to infer
the schema from an XML document, enables you to specify the namespaces to be ignored when inferring
the schema. This method takes two parameters. The first parameter is an XML document location, a
stream, or an
XmlReader; the second parameter is a string array of the namespaces that need to be
ignored when inferring the schema.
Transforming DataSet to XML
You can easily fill a DataSet with data from an XML stream or document. The information supplied
from the XML stream or document can be combined with existing data or schema information that is
already present in the
DataSet. On the other side, you can use the WriteXml() method of the DataSet
object to serialize the XML representation of the DataSet to a file, a stream, an XmlWriter object, or a
string. While serializing the contents, you can optionally include the schema information.

To control the actual behavior of the
WriteXml() method, you set the XmlWriteMode enumeration to
any of the values shown in Table 8-2. The values supplied to the
XmlWriteMode enum determine the
layout of the XML output. The
DataSet representation includes tables, relations, and constraints definitions.
The rows in the
DataSet’s tables are written in their current versions unless you choose to employ the
DiffGram format. Table 8-2 summarizes the writing options available with XmlWriteMode.
Table 8-2. Members of XmlWriteMode Enumeration
Member Description
DiffGram Allows you to write the entire DataSet as a DiffGram, including original
and current values
IgnoreSchema Writes the contents of the DataSet as XML data, without an XSD schema
WriteSchema Writes the contents of the DataSet as XML data with relational structure
as inline XSD schema
Using ADO.NET, you can create an XML representation of a DataSet, with or with-
out its schema, and transport the
DataSet across HTTP for use by another application
or XML-enabled platform. In an XML representation of a
DataSet, the data is written
in XML and the schema is written using the XML Schema definition language (XSD).
Using industry standards such as XML and XML schema, you can seamlessly interact
with XML-enabled applications that may be running in a completely different
platform.
222
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 222
As you can see, ADO.NET allows you to write XML data with or without the XML schema. When you
write

DataSet data as XML data, the current version of the DataSet rows is written; however,
ADO.NET enables you to write the
DataSet data as a DiffGram, which means that both original as
well as current versions of the rows would be included. Before proceeding further with an example, it is
important to understand what
DiffGrams are.
DiffGrams
A DiffGram is in XML format and is used by the DataSet to store the contents. A DiffGram is used
to discriminate between the original and current versions of data. When you write a
DataSet as a
DiffGram, the DiffGram is populated with all information that is required to re-create the contents of
the dataset. These contents include the current and original values of the rows and the error information
and order of the rows; however, the
DiffGram format doesn’t get populated with the information to re-
create the XML schema. A
DataSet also uses the DiffGram format to serialize data for transmission
across the network.
The
DiffGram format consists of the following data blocks:

<DataInstance> represents a row of the DataTable object or a dataset and contains the current
version of data.

<diffgr:before> contains the original version of the dataset or a row.

<diffgr:errors> contains the information of the errors for a specific row in the
<DataInstance> block.
Note the element or row that has been edited or modified is marked with the <diff:hasChanges> annota-
tion in the current data section. Now that you have the basic knowledge of
DiffGram, you will learn how

you can write a dataset as XML data. If you want to write the XML representation of the
DataSet to an
XmlWriter, a stream, or a file, you need to use the WriteXml() method. The WriteXml() method takes
two parameters. The first parameter is mandatory and is used to specify the destination of XML output. The
second parameter is optional and is used to specify how the XML output would be written. The second
parameter of the
WriteXml() method is the XmlWriteMode enumeration to which you can pass in any of
the values shown in Table 8-2.
Listing 8-4 shows you an example on how to serialize the DataSet to an XML file using the WriteXml()
method of the DataSet. It also shows how easily you can cache the contents of the DataSet in a local
XML file that obviates the need to retrieve the data from the database every time.
Listing 8-4: Serializing DataSet to XML Using WriteXml
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
<%@ Import Namespace=”System.IO”%>
<script runat=”server”>
The DiffGram format is used by default when you send and extract a DataSet from a
Web service. In addition, you can explicitly specify that the dataset be read or written
as a
DiffGram when using the ReadXml() and WriteXml() methods.
223
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 223
void Page_Load(Object sender, EventArgs e)
{
DataSet contactsDataSet;
string filePath = Server.MapPath(“App_Data/ContactType.xml”);
//Check if the file exists in the hard drive

if (File.Exists(filePath))
{
contactsDataSet = new DataSet();
//Read the contents of the XML file into the DataSet
contactsDataSet.ReadXml(filePath);
}
else
{
contactsDataSet = GetContactTypes();
//Write the contents of the DataSet to a local XML file
contactsDataSet.WriteXml(filePath);
}
gridContacts.DataSource = contactsDataSet.Tables[0].DefaultView;
gridContacts.DataBind();
}
DataSet GetContactTypes()
{
string connString = WebConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select ContactTypeID, Name from Person.ContactType”;
DataSet contactsDataSet = new DataSet(“ContactType”);
using (SqlConnection sqlConn = new SqlConnection(connString))
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactsDataSet, “Contact”);
}
return contactsDataSet;
}
</script>
<html xmlns=” >

<head runat=”server”>
<title>Serializing a DataSet object to XML</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView id=”gridContacts” runat=”server”
AutoGenerateColumns=”False” CellPadding=”4”
HeaderStyle-BackColor=”blue” HeaderStyle-ForeColor=”White”
HeaderStyle-HorizontalAlign=”Center” HeaderStyle-Font-Bold=”True”>
<Columns>
<asp:BoundField HeaderText=”Contact Type ID” DataField=”ContactTypeID”/>
<asp:BoundField HeaderText=”Name” DataField=”Name”
ItemStyle-HorizontalAlign=”Right”/>
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
224
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 224
The Page_Load event shown in Listing 8-4 starts by checking for the presence of an XML file named
ContactType.xml. If the file is not present, it invokes a private helper method named GetContactTypes()
that retrieves contract types from the database. After retrieving the contract types in the form of a
DataSet, it then saves the contents of the DataSet into a local XML file named ContactType.xml. During
subsequent requests, this local XML file is used to display the contact types information. Finally, the con-
tents of the
DataSet are displayed through a GridView control.

Controlling the Rendering Behavior of DataColumn Objects
When you serialize a DataSet object to XML, the contents in the DataSet are rendered either as elements
or attributes. It is possible to control this rendering behavior by setting appropriate attributes at the
DataColumn object level. The DataColumn object has a property called ColumnMapping that determines
how columns are rendered in XML. The
ColumnMapping property takes values from the MappingType
enum. Table 8-3 summarizes the various values supported by the MappingType enum.
Table 8-3. Members of MappingType Enumeration
Member Description
Element Allows you to map a column to an element. This is the default
behavior.
Attribute Allows you to map a column to an attribute.
Hidden Allows you to map a column to an internal structure.
SimpleContent Allows you to map a column to an XmlText node.
The following code demonstrates the purpose of the
MappingType enumeration by setting the
ColumnMapping property for all the columns in the ContactType table to MappingType.Attribute.
for (int i = 0; i < contactsDataSet.Tables[0].Columns.Count; i++)
{
contactsDataSet.Tables[0].Columns[i].ColumnMapping =
MappingType.Attribute;
}
This results in all the columns of the contact types table being rendered in the form of attributes when
the
DataSet is serialized to XML format.
Modifying the Table and Column Names
By default, when you use the DataAdapter to fill a DataSet, the column names that are used in the
DataSet correspond to the column names defined in the data source. When you serialize the DataSet,
the elements in the XML output exactly match the column names in the original
DataSet. There are

times where the default XML elements may not work for you and you might need the ability to tailor the
XML elements. One way of solving this problem is to use some sort of mapping. With ADO.NET, there
are two places that you can implement mapping: at the query level, or in the
DataAdapter.
225
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 225
The SQL language provides a basic ability to change column names using the AS keyword. For example,
the following query selects three columns from the
ContactType table, and renames two of them.
SELECT ContactTypeID AS ID, Name AS ContactTypeName,
ModifiedDate FROM Person.ContactType
This technique is useful if you are following good design practices and placing your query in a view or
stored procedure in the database. If column names change, you can simply update the corresponding
view or stored procedure, and the client application will continue to work seamlessly.
The AS keyword isn’t perfect, though. The most obvious drawback is that you can only use the AS
keyword in a query. Another approach to changing the column names is using the column mapping
technique in ADO.NET. The basic principle is to apply a list of column transformations to the
DataAdapter object. When you fill a DataSet using the DataAdapter, it automatically renames the
source columns and uses the names you have configured. Here’s an example:
//Create the new mapping.
DataTableMapping map;
map = adapter.TableMappings.Add(“ContactType”, “ContactType”);
//Define column mappings.
map.ColumnMappings.Add(“ContactTypeID”, “ID”);
map.ColumnMappings.Add(“Name”, “ContactTypeName”);
map.ColumnMappings.Add(“Description”, “Description”);
//Fill the DataSet.
adapter.Fill(ds, “ContactType”);
You may notice that the DataAdapter mappings also give you the opportunity to map table names. This

isn’t very useful in this context, however, because the table name is never drawn from the data source;
instead, it is supplied as a parameter for the
Fill() method. In the previous example, the table name

ContactType” is mapped to the DataTable named “ContactType” — in other words, the name is not
changed. However, this step is still required. If no parameter is specified, the default name (“Table”) is
used. Note that this technique is useful if you are directly filling a
DataTable and serializing it to XML.
Of course, nothing prevents you from using table mappings, if you want. In the following example, the
Fill() method actually creates a table in the DataSet named ContactTypeList, not ContactType.
//Create the new mapping.
DataTableMapping map;
map = adapter.TableMappings.Add(“ContactType”, “ContactTypeList”);
//Fill the DataSet.
adapter.Fill(ds, “ContactType”);
One case in which table mapping can be useful is if you have a stored procedure or batch query that returns
multiple result sets. In this case, a number is automatically added to the table name for each subsequent
result set, as in
ContactType, ContactType1, ContactType2, and so on. By adding a DataTableMapping
object for each of these tables, you can correct this behavior:
// Create a mapping that returns a list of ContactTypes and a list of Contacts
DataTableMapping map;
adapter.TableMappings.Add(“Results”, “ContactType”);
adapter.TableMappings.Add(“Results1”, “Contact”);
//Fill the DataSet using a stored procedure that returns multiple result sets
adapter.Fill(ds, “Results”);
226
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 226
The DataAdapter provides a special MissingMappingAction property that governs how it behaves if

you do not supply column and table mappings. It takes one of the
MissingMappingAction values
described in Table 8-4.
Table 8-4. Values of MissingMappingAction Enumeration
Value Description
Error If there is any column that doesn’t have a mapping, an exception is
thrown
Ignore All columns that do not have a mapping are ignored, and not added
to the
DataSet
Passthrough If there is any column that doesn’t have a mapping, the data source
column name is used; this is the default
For example, to raise an error whenever there is a column that does not have mapping, set the
MissingMappingAction property to MissingMappingAction.Error as shown here:
adapter.MissingMappingAction = MissingMappingAction.Error;
Getting XML as a String from a DataSet
It is also possible to serialize the contents of a DataSet into a string for use in your code. Although it is
not a recommended technique for extracting XML from a
DataSet, it can be useful in situations where
you want to use the
DataSet object as a string in your code. To accomplish this, use the GetXml() and
GetXmlSchema() methods of the DataSet object. Listing 8-5 shows an example of how to retrieve XML
data from a
DataSet directly using GetXml() and GetXmlSchema() methods.
Listing 8-5: Retrieving XML as a String from a DataSet
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
<%@ Import Namespace=”System.IO”%>

<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{
string connString = WebConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select TOP 2 * from Person.ContactType”;
DataSet contactsDataSet = new DataSet();
using (SqlConnection sqlConn = new SqlConnection(connString))
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactsDataSet);
}
//Assign the contents to literal controls
ltlXmlData.Text = Server.HtmlEncode(contactsDataSet.GetXml());
ltlXmlSchema.Text = Server.HtmlEncode(contactsDataSet.GetXmlSchema());
}
</script>
227
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 227
<html xmlns=” >
<head runat=”server”>
<title>Getting XML as a String from a DataSet</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:Literal runat=”server” ID=”ltlXmlData”></asp:Literal>
<br/><br/><br/>
<asp:Literal runat=”server” ID=”ltlXmlSchema”></asp:Literal>

</div>
</form>
</body>
</html>
The code shown in Listing 8-5 starts by retrieving ContactType details from AdventureWorks database
into a
DataSet. After retrieving the data in the form of a DataSet, it then retrieves XML data and its
associated schema using
GetXml() and GetXmlSchema() methods from the DataSet and displays
them in literal controls.
Nesting XML Output from a DataSet
As you know, a DataSet can contain more than one table and also the relationships between these
tables. In an ADO.NET dataset,
DataRelation is used to implement the relationship between tables
and work with the child rows from one table that are related to a specific row in the parent table. XML
provides a hierarchical representation of data in which the parent entities contain nested child entities.
The
DataRelation object has a property named Nested. This property is false by default and has no
effect when you are accessing the data using relational techniques. It does, however, affect the way the
data is exported as XML when you save the contents of the
DataSet using WriteXml() method.
The code shown in Listing 8-6 demonstrates how to create nested XML output from a
DataSet using an
XmlDataDocument object.
Listing 8-6: Generating Nested XML Output from a DataSet
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Xml”%>

DataRelation relates two DataTable objects by using DataColumn objects. These
relationships are created between the matching records in the parent and child
tables. The
DataType value of these columns should be identical. The referential
integrity between the tables is maintained by adding the
ForeignKeyConstraint to
ConstraintCollection of the DataTable object. Before a DataRelation is created,
it first checks whether a relationship can be established. If a relationship can be estab-
lished, a
DataRelation is created and then added to the DataRelationCollection.
You can then access the
DataRelation objects from the DataRelationCollection by
using the
Relations property of the dataset and the ChildRelations and
ParentRelations properties of the DataTable object.
228
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 228
<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{
string filePath = Server.MapPath(“NestedOutput.xml”);
//Get the values from the database
DataSet prodCategoriesDataSet = GetCategoriesAndProducts();
XmlDataDocument xmlDoc = new XmlDataDocument(prodCategoriesDataSet);
//Write the dataset
xmlDoc.DataSet.WriteXml(filePath);
hlkDataSetOutput.NavigateUrl = “NestedOutput.xml”;
}
DataSet GetCategoriesAndProducts()

{
string connString = WebConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select * from Production.ProductSubCategory;” +
“Select * from Production.Product”;
DataSet prodCategoriesDataSet = new DataSet();
using (SqlConnection sqlConn = new SqlConnection(connString))
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(prodCategoriesDataSet);
//Name the DataTables properly
prodCategoriesDataSet.Tables[0].TableName = “ProductSubCategories”;
prodCategoriesDataSet.Tables[1].TableName = “Products”;
prodCategoriesDataSet.Relations.Add(
prodCategoriesDataSet.Tables[0].Columns[“ProductSubCategoryID”],
prodCategoriesDataSet.Tables[1].Columns[“ProductSubCategoryID”]);
prodCategoriesDataSet.Relations[0].Nested = true;
}
return prodCategoriesDataSet;
}
</script>
<html xmlns=” >
<head runat=”server”>
<title>Nested XML Output from a DataSet Object</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:HyperLink ID=”hlkDataSetOutput” Runat=”server”>
Click here to view the output of the Serialized DataSet Output

</asp:HyperLink>
</div>
</form>
</body>
</html>
The key point to note in Listing 8-6 is the line of code where you set the Nested property of the
DataRelation object to true.
prodCategoriesDataSet.Relations[0].Nested = true;
229
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 229
Opening up the XML file produced by the code in Listing 8-6 results in an output that is somewhat similar
to Figure 8-2.
Figure 8-2
As you can see from the output, each
<ProductSubCategories> element is a child of the document root,
and the
<Products> elements are nested within their respective <ProductSubCategories> elements.
Writing DataSet Schema
In addition to writing out XML data from a DataSet, you can also write out the schema of the DataSet.
The
DataSet enables you to write a DataSet schema through the WriteXmlSchema() method, facilitating
the transportation of this schema in an XML document. This method takes one parameter, which specifies
the location of the resulting XSD schema, and the location can be a file, an
XmlWriter, or a Stream.
Typed DataSets
A typed DataSet is a subclass of DataSet class in which the tables that exist in the DataSet are derived by
reading the XSD schema information. The difference between a typed
DataSet and an ordinary DataSet is
that the

DataRows, DataTables, and other items are available as strong types; that is, rather than refer to
MyDataSet.Tables[0] or MyDataSet.Tables[“customers”], you code against a strongly typed
DataTable named, for example, MyDataSet.Customers. Typed DataSets have the advantage that the
strongly typed variable names can be checked at compile time rather than causing errors at runtime.
230
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 230
Suppose you have a DataSet that contains a table named customers, which has a column named
CompanyName. You can refer to the table and the column by ordinal or by name as shown here.
ds.Tables[“Customers”].Rows[0].Columns[“CompanyName”] = “XYZ Company”;
As you can see, the data is loosely typed when referred to by ordinal or name, meaning that the compiler
cannot guarantee that you have spelled the column name correctly or used the correct ordinal. The problem
is that the error informing you of this occurs at runtime rather than at compile time. If the
DataSet items
were strongly typed, misspelling the column name or using the wrong ordinal would be prevented because
the code simply would not compile. The following code shows you how to assign the same value to the
same field but using a typed
DataSet.
ds.Customers[0].CompanyName = “XYZ Company”;
Now the table name and the field to be accessed are not treated as string literals, but instead are encased
in an XML schema and a class that is generated from the
DataSet class. When you create a typed
DataSet, you are creating a class that implements the tables and fields based upon the schema used to
generate the class. Basically, the schema is coded into the class.
As you compare the two examples, you see that a typed
DataSet is easier to read and understand. It is
less error-prone, and errors are realized at compile time as opposed to runtime.
Generating Typed DataSets
The easiest way to generate a typed DataSet is to use Visual Studio. In Visual Studio, just right-click
on the existing table, stored procedure, SQL statement and select “

Generate DataSet.” The .NET
Framework SDK also ships with a tool called XSD.exe that provides you with more control and options
for generating the XML schema.
Instead of using Visual Studio, you also have the option of generating the typed
DataSet manually
using the following steps.
1. Fill a DataSet with data from the database
2. Save the schema with DataSet.WriteXmlSchema()
3. Use the schema as input into XSD.exe
231
XML and ADO.NET
The XML Schema Definition tool (Xsd.exe) is a utility that ships with .NET
Framework SDK. This tool is designed primarily for two purposes:
Allows you to generate either C# or Visual Basic class files that conform to a specific
XML schema definition language (XSD) schema. The tool takes an XML schema as
an argument and outputs a file that contains a number of classes. After you generate
these classes and serialize them using XmlSerializer class, the output XML will be
compliant with the XSD schema.
Allows you to generate an XML schema document from a .dll file or .exe file. You can
accomplish this by passing in the DLL or EXE as an argument to the tool, and you
will get the XML schema as the output .Note that in this case, the XSD.exe tool is
looking for
System.Xml.XmlSerializer compatible types, meaning that it is look-
ing for types with public fields, or public properties with both getter and setters.
11_596772 ch08.qxd 12/13/05 11:14 PM Page 231
For example, generate a typed DataSet for the product table and see what you get.
SqlDataAdapter da = new SqlDataAdapter(“select * from Production.Product “ +
“ as Product”,
“server=localhost;integrated security=true;database=AdventureWorks”);
//Name the DataSet ProductsDataSet

DataSet ds = new DataSet(“ProductsDataSet”);
//Name the table ProductsTable
da.Fill(ds, “ProductsTable”);
ds.WriteXmlSchema(“Products.xsd”);
After you have the Products.xsd file generated, you can then utilize the XSD.exe utility to generate
the typed
DataSet.
xsd /DataSet /language:CS C:\Data\Products.xsd
The previous command in the command prompt creates a typed DataSet called ProductsDataSet in
the
Products.cs file. Listing 8-7 shows a brief version of the ProductsDataSet.
Listing 8-7: Typed DataSet
[Serializable()]
[System.ComponentModel.DesignerCategoryAttribute(“code”)]
[System.ComponentModel.ToolboxItem(true)]
[System.Xml.Serialization.XmlSchemaProviderAttribute(“GetTypedDataSetSchema”)]
[System.Xml.Serialization.XmlRootAttribute(“ProductsDataSet”)]
[System.ComponentModel.Design.HelpKeywordAttribute(“vs.data.DataSet”)]
public partial class ProductsDataSet : System.Data.DataSet {
private ProductsTableDataTable tableProductsTable;
private System.Data.SchemaSerializationMode _schemaSerializationMode =
System.Data.SchemaSerializationMode.IncludeSchema;
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public ProductsDataSet() {
this.BeginInit();
this.InitClass();
System.ComponentModel.CollectionChangeEventHandler schemaChangedHandler =
new System.ComponentModel.CollectionChangeEventHandler(
this.SchemaChanged);
base.Tables.CollectionChanged += schemaChangedHandler;

base.Relations.CollectionChanged += schemaChangedHandler;
this.EndInit();
}


[System.Serializable()]
[System.Xml.Serialization.XmlSchemaProviderAttribute(“GetTypedTableSchema”)]
public partial class ProductsTableDataTable : System.Data.DataTable,
System.Collections.IEnumerable {
private System.Data.DataColumn columnProductID;
private System.Data.DataColumn columnName;
private System.Data.DataColumn columnProductNumber;
private System.Data.DataColumn columnMakeFlag;


}
232
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 232
public partial class ProductsTableRow : System.Data.DataRow {
private ProductsTableDataTable tableProductsTable;


}
public class ProductsTableRowChangeEvent : System.EventArgs {
private ProductsTableRow eventRow;


}
}

The typed DataSet accomplishes strong typing by generating a class ProductsDataSet, which derives
from
DataSet class. The name of the subclass of the DataSet class is equal to DataSet.DataSetName
in the original DataSet that produced the XML schema. As you can see from the previous code listing,
four public nested classes are exposed:

ProductsDataSet
❑ ProductsTableDataTable
❑ ProductsTableRow
❑ ProductsTableRowChangeEvent
A strongly typed DataSet can also contain more than one table. If you have tables with parent-child
relationships—specified by the existence of a
DataRelation in the DataSet’s Relations collection —
some additional information and methods are generated. When the
DataSet contains a Relation, the fol-
lowing happens:
❑ The
PrimaryKey property is added to DataColumn properties for the parent table.
❑ A
ForeignKeyConstraint is added for the child table.

DataRelation is added.
If the
DataSet’s Nested property was set in the original schema, it is preserved in the typed DataSet.
Using Typed DataSet
In most cases, you can use your Typed DataSet everywhere you would normally use a DataSet. Your
Typed
DataSet directly derives from DataSet so all the DataSet-related classes and methods will
work. One of the distinct advantages of using a Typed
DataSet is that elements of the DataSet are

strongly typed and strongly named. The use of the new class is a little different than our old
DataSet
examples, as follows:
ProductsDataSet prodDS = new ProductsDataSet();
SqlDataAdapter adapter = new SqlDataAdapter(“SELECT * FROM Production.Product “ +
“ As Product”,
“server=localhost;Integrated Security=true;Initial Catalog=AdventureWorks”);
adapter.Fill(prodDS, “ProductsTable”);
foreach (ProductsDataSet.ProductsTableRow row in prodDS.ProductsTable.Rows)
{
Response.Write(row.ProductID);
}
233
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 233
When filling the typed DataSet with data, you can expect the same behavior as that of the DataSet because
the typed
DataSet directly derives from the base DataSet class. As the previous code shows, after you fill
it, you can use the typed accessors to get at rows and columns in a more direct way than with an untyped
DataSet.
Using Annotations with a Typed DataSet
Although strongly typed DataSets are produced using the names in the schema, you can refine the
naming process by using certain schema annotations. These attributes are specified on the element dec-
laration that equates to the table. The annotations are as follows:

typedName: Name of an object referring to a row

typedPlural: Name of an object referring to a table

typedParent: Name of a parent object in a parent-child relationship


typedChild: Name of a child object in a parent-child relationship
There is also an annotation,
nullValue, that refers to special handling in a strongly typed DataSet
when the value in the underlying table is DBNull.
For example, consider the XSD schema.
<xs:element name=”ProductsTable”>
<xs:complexType>
<xs:sequence>
<xs:element name=”ProductID” type=”xs:int” minOccurs=”0” />
</xs:sequence>
</xs:complexType>
</xs:element>
The previous schema element for the ProductsTable that represents the Products table of the
AdventureWorks database would result in a
DataRow name of ProductsTableRow. In common scenar-
ios, the object would be referred to without the Row identifier and instead would be simply referred to
as a Product object. Similarly, you would want to refer to the collection of Products simply as Products.
To accomplish this, you need to annotate the schema and identify new names for the
DataRow and
DataRowCollection objects. The following code shows the annotated schema that will produce the
desired output.
<xs:element name=”ProductsTable” codegen:typedName=”Product”
codegen:typedPlural=”Products”>
<xs:complexType>
<xs:sequence>
<xs:element name=”ProductID” type=”xs:int” minOccurs=”0” />
</xs:sequence>
</xs:complexType>
</xs:element>

Now that you have set the codegen:typedName and codegen:typedPlural attributes to Product,
Products respectively, if you regenerate the
DataSet using XSD, you will find that the generated
DataSet reflects the changes. Note that in the previous XML schema, there is a new prefix called code-
gen for which you need to add the following namespace declaration to the
xsd:schema element.
xmlns:codegen=”urn:schemas-microsoft-com:xml-msprop”
234
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 234
The following code shows how you can loop through the DataSet using the new DataRow and
DataRowCollection object names.
ProductsDataSet prodDS = new ProductsDataSet();
SqlDataAdapter adapter = new SqlDataAdapter(“SELECT * FROM Production.Product “ +
“ as Product”,
“server=localhost;Integrated Security=true;Initial Catalog=AdventureWorks”);
adapter.Fill(prodDS, “ProductsTable”);
foreach (ProductsDataSet.Product prod in prodDS.Products)
{
Response.Write(prod.ProductID);
}
Note that the previous code uses Product and Products to refer to the DataRow and
DataRowCollection instances of the DataTable.
XmlDataDocument Object and DataSet
The key XML class that makes it possible to access both relational and hierarchical data in a consistent
manner is the
XmlDataDocument class. The XmlDataDocument class inherits from the base class
XmlDocument and differs from it only in the ability to synchronize with DataSet objects. When synchro-
nized,
DataSet and XmlDataDocument classes work on the same collection of rows, and you can apply

changes through both interfaces (nodes and relational tables) and make them immediately visible to
both classes. Basically,
DataSet and XmlDataDocument provide two sets of tools for the same data. As a
result, you can apply XSLT transformations to relational data, query relational data through XPath
expressions, and use SQL to select XML nodes. It can also be useful in situations when you want to
retain the full fidelity of fairly unstructured XML and still use methods provided by the
DataSet object.
Because the
XmlDataDocument class is derived from the XmlDocument class, it supports all the proper-
ties and methods of the
XmlDocument class. Additionally, the XmlDataDocument class has its own prop-
erties and methods for providing a relational view of the data contained in it. Table 8-5 discusses the
properties and methods of the
XmlDataDocument in this context.
Table 8-5. Important Properties and Methods of the XmlDataDocument Class
Property or Method Description
DataSet This property returns reference to the DataSet that provides a
relational representation of the data in the
XmlDataDocument
GetElementFromRow This method retrieves the XmlElement associated with the specified
DataRow
GetRowFromElement
This method returns reference to the DataRow associated with the
specified
XmlElement
Load
This method loads the XmlDataDocument using the specified data
source and synchronizes the
DataSet with the loaded data
235

XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 235
Data can be loaded into an XmlDataDocument through either the DataSet interfaces or the
XmlDocument interfaces. You can import the relational part of the XML document into DataSet by
using an explicit or implied mapping schema. Whether changes are made through
DataSet or through
XmlDataDocument, the changed values are reflected in both objects. The full-fidelity XML is always
available through the
XmlDataDocument.
Associating an XmlDataDocument with a DataSet
There are a few ways to bind a DataSet object and XmlDataDocument object together. The first option is
that you pass a non-empty
DataSet object to the constructor of the XmlDataDocument class as follows.
XmlDataDocument xmlDoc = new XmlDataDocument(dataset);
Similar to its base class, XmlDataDocument provides a XML DOM approach to work with XML data. An
alternate way of synchronizing the two objects is by creating a valid and non-empty
DataSet object
from a non-empty instance of the
XmlDataDocument object. An example of this is illustrated here.
XmlDataDocument xmlDoc = new XmlDataDocument();
xmlDoc.Load(filePath);
DataSet dataset = xmlDoc.DataSet;
You can turn an XmlDataDocument into a DataSet object using the XmlDataDocument’s DataSet
property. The property instantiates, populates, and returns a DataSet object. The DataSet is associated
with the
XmlDataDocument the first time you access the DataSet property. To view the XML data rela-
tionally, you must first specify a schema to use for data mapping. This can be done by calling the
ReadXmlSchema() method on the same XML file. As an alternate approach, you can manually create
the necessary tables and columns in the
DataSet.

Keeping the two objects synchronized provides an unprecedented level of flexibility by allowing you to
use two radically different types of navigation to move through records. In fact, you can use SQL-like
queries on XML nodes, as well as XPath queries on relational rows. Keep in mind, however, that not all
XML files can be successfully synchronized with a
DataSet. For this to happen, XML documents must
have a regular, tabular structure that can be mapped to a relational architecture where each row has the
same number of columns. Also remember that when rendered as
DataSet objects, XML documents lose
any XML-specific information they may have and for which there isn’t a relational counterpart. This
information includes comments, declarations, and processing instructions.
Loading a DataSet through an XmlDataDocument
You can load a DataSet through either the DataSet interfaces or the XmlDataDocument interfaces. For
example, you can import the relational part of the XML document into
DataSet by using an explicit or
implied mapping schema. One of the important advantages of this approach is that whether the changes
are made through
DataSet or through XmlDataDocument, the changed values are reflected in both
objects. Moreover, you can obtain the full fidelity XML any time through the
XmlDataDocument. Listing
8-8 illustrates the code required for loading a
DataSet through an XmlDataDocument.
Listing 8-8: Using an XmlDataDocument to Load a DataSet
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
236
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 236
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Xml”%>

<script runat=”server”>
void Page_Load(Object sender, EventArgs e)
{
string xmlPath = Server.MapPath(“App_Data/ContactType.xml”);
string xmlSchemaPath = Server.MapPath(“App_Data/ContactType.xsd”);
SaveContacts(xmlPath, xmlSchemaPath);
XmlDataDocument xmlDoc = new XmlDataDocument();
xmlDoc.DataSet.ReadXmlSchema(xmlSchemaPath);
xmlDoc.Load(xmlPath);
DataSet contactsDataSet = xmlDoc.DataSet;
//Bind the DataSet to the DataGrid object
gridContacts.DataSource = contactsDataSet.Tables[0].DefaultView;
gridContacts.DataBind();
}
void SaveContacts(string xmlPath, string xmlSchemaPath)
{
string connString = WebConfigurationManager.
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select * from Person.ContactType”;
DataSet contactsDataSet = new DataSet(“ContactTypes”);
using (SqlConnection sqlConn = new SqlConnection(connString))
{
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactsDataSet);
}
contactsDataSet.WriteXml(xmlPath);
contactsDataSet.WriteXmlSchema(xmlSchemaPath);
}
</script>
<html xmlns=” >

<head runat=”server”>
<title>Loading a DataSet through an XmlDataDocument</title>
</head>
<body>
<form id=”form1” runat=”server”>
<div>
<asp:GridView id=”gridContacts” runat=”server”
AutoGenerateColumns=”False” CellPadding=”4”
HeaderStyle-BackColor=”blue” HeaderStyle-ForeColor=”White”
HeaderStyle-HorizontalAlign=”Center” HeaderStyle-Font-Bold=”True”>
<Columns>
<asp:BoundField HeaderText=”Contact Type ID”
DataField=”ContactTypeID” />
<asp:BoundField HeaderText=”Name”
DataField=”Name”
ItemStyle-HorizontalAlign=”Right” />
</Columns>
</asp:GridView>
</div>
</form>
</body>
</html>
237
XML and ADO.NET
11_596772 ch08.qxd 12/13/05 11:14 PM Page 237
In the Page_Load event, an XmlDataDocument object is instantiated. After that, the ReadXmlSchema()
method of the DataSet object is used to load the XML schema. After the schema is loaded, the actual
XML data is then loaded using the
Load() method of XmlDataDocument object. Now that the schema as
well as the XML data is loaded into the

XmlDataDocument object, you can now retrieve the DataSet
using the DataSet property of the XmlDataDocument object. Finally, this DataSet object is bound to a
GridView control. Navigate to the page using the browser, and you should see an output very similar to
Figure 8-3.
Figure 8-3
Extracting XML Elements from a DataSet
When you are interacting with a DataSet, you may want to extract individual rows within a DataSet
object as an XML element. To accomplish this, perform the following steps.
1. Create a DataSet object and fill it with the values from a database table.
2. Associate an XmlDataDocument object with the DataSet by passing in the DataSet object to
the constructor of the
XmlDataDocument object.
3. Invoke the GetElementFromRow() method of the XmlDataDocument object and pass in the
DataRow object as an argument. The GetElementFromRow() method returns an XML represen-
tation of the
DataRow object in the form of an XmlElement object.
Listing 8-9 shows an example of how to extract XML data using the
GetElementFromRow() method of
the
XmlDataDocument object.
Listing 8-9: Extracting XML Elements from an XmlDataDocument Object
<%@ Page Language=”C#” %>
<%@ Import Namespace=”System.Web.Configuration”%>
<%@ Import Namespace=”System.Data.SqlClient”%>
<%@ Import Namespace=”System.Data”%>
<%@ Import Namespace=”System.Xml”%>
238
Chapter 8
11_596772 ch08.qxd 12/13/05 11:14 PM Page 238

×