Now that you have an understanding of the DataSet, the next section goes into more detail on how topopulate a DataSetwith XML data from an XML document and how to save the contents of a
Trang 1Let’s start by discussing the DataSetobject and the XML support it offers.
ADO.NET and XML
One of the key objects in ADO.NET is the DataSet ASystem.Data.DataSetobject stores data in a chical object model and hence is similar to a relational database in structure Besides storing data in a discon-nected cache, a DataSetalso stores information such as the constraints and relationships that are definedfor the DataSet You use a DataSetto 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 DataSetproperties, methods, and events; and the XML DOM through the use of XmlDataDocumentobject Both ofthese techniques have parallel access methods that permit you to follow sequential or hierarchical pathsthrough your data
hierar-ADO.NET supports the ability to construct DataSetobjects from either XML streams or documents.These XML sources can include data or schema, or both The schema is expressed as Extensible SchemaDefinition language (XSD) — another form of XML You can also export data from a DataSet to an XMLdocument, with or without the schema This is handy when you have to send data through a firewall; inmost 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 topopulate a DataSetwith 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 DataSetwith 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 DataSetwith the XML in the stream object — that is, anyobject that inherits from System.IO.Stream, such as System.IO.FileStream; however, itcould just as easily be a stream of data coming down from a Web site, and so on
❑ ReadXml(String): This loads the DataSetwith the XML stored in the file whose name youprovide
❑ ReadXml(TextReader): This loads the DataSetwith the XML processed by the given textreader — that is, any object that inherits from System.IO.TextReader
❑ ReadXml(XmlReader): This loads the DataSetwith the XML processed by the given XMLreader As you’ve seen, the XmlValidatingReaderclass inherits from
System.Xml.XmlReader, so you can pass an XmlValidatingReaderto this function
ADataSetcan either be a typed DataSet or an untyped DataSet A typed DataSet
is a class derived from a DataSetclass and has an associated XML Schema On the
other hand, an untyped DataSetdoes not have an associated XML Schema You see
more on typed DataSetslater in this chapter.
214
Trang 2The 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.XmlReadModeenumeration is used to determine the behavior of the XML parser whenloading documents from various sources Table 8-1 shows the members of the XmlReadModeenumeration,and the impact they have on the DataSetand how it loads the XML
Table 8-1 Members of XmlReadMode Enumeration
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 XmlReadModeis set to DiffGram If the DataSethasalready been given a schema by some means, or the XMLdocument has an inline schema defined, the XmlReadModeis set
to ReadSchema If the DataSetdoesn’t contain a schema, there
is no inline schema defined and the XML document is not aDiffGram, the XmlReadModeis set to InferSchema Because ofthis indirection, the XmlReadMode.Automay be slower thanusing 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 theDataSetprior to this operation, the schema can be extended
by the inline XML schema If new table definitions exist in theinline schema that already exists in the DataSet, however, anexception will be thrown
IgnoreSchema Ignores any inline schema and loads the data into the existing
DataSetschema Any data that does not match the schema isdiscarded If you are bulk loading data from existing XMLsources, it might be useful to enable this option to get betterperformance
InferSchema This option forces the DataSetto 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 DataSetloads a DiffGramand 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 with-outputgenerated by FOR XML queries In this option, the defaultnamespace is read as the inline schema
215
Trang 3Now that you have some information on the ReadXml()method and how to process XML, load someXML data into a DataSet Before looking at the code, it is useful to examine the XML file (namedProducts.xml) to be used It is as follows:
Listing 8-1: Reading XML into a DataSet Using ReadXml
string filePath = Server.MapPath(“App_Data/Products.xml”);
productsDataSet = new DataSet();
//Read the contents of the XML file into the DataSetproductsDataSet.ReadXml(filePath);
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
Trang 4Figure 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 DataSetand then call ReadXml() Finally, call DataTable.EndLoadData()for each table in the DataSetas shown in the following example.
217
Trang 5foreach (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 DataSetwith XML data by using the
ReadXml()method of the DataSet object But what do you do when you want to load a DataSetschema from an XML document?
You use either the ReadXmlSchema()or the InferXmlSchema()method of the DataSetto loadDataSetschema 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 DataSetobject without an existing data structureattempts to load data from an XML document The DataSetwill make an initial pass through the XMLdocument to infer the data structure and then a second pass to load the DataSetwith the information contained in the document There is a set of rules for inferring DataSetschemas 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 DataSetfollows 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 becolumns, 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 columncalled Tablename_Textis created for the text of each of the elements If an element with bothchild 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, anested DataRelationis created between the two tables
218
Trang 6Inference Rules in Action
Consider a sample XML document to understand what kind of schema the DataSetwill infer from thatXML document
Listing 8-2: XSD Schema Produced through Schema Inference
<xs:element name=”ProductID” type=”xs:string” minOccurs=”0” />
<xs:element name=”ProductName” type=”xs:string” minOccurs=”0” />
Supplied Schemas
Instead of allowing the DataSetto infer the schema, you can supply a schema to a DataSetexplicitly.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 beorganized It can’t infer data types, and it won’t infer existing column relationships — instead, it createsnew columns and new relationships
219
Trang 7Because of the inherent problems related to schema inference, there are times you might want to
explicitly supply a schema to your DataSetobject There are several ways you can accomplish this.Obviously, you can create one yourself by creating tables and columns in a DataSetobject and by usingthe WriteXmlSchema()method as described previously Alternatively, you can supply an XSD file (or anXmlSchemaclass) to the DataSet, or you can give the responsibility of generating the internal relationalstructure 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 DataAdapterhas a FillSchema()method that executes a query on the database to fill the schemainformation of table/tables into a DataSet For an example of a situation in which this facility might beuseful, imagine that you have an XML document whose data you would eventually like to add to a SQLServer database As a first step toward doing that, you simply fill the DataSetwith the schema of theContactTypetable and then load the XML document with the DataSet’s ReadXml()method Afterthat, you can easily use the DataSetto add to the SQL Server database Before looking at the ASP.NETpage, examine the ContactType.xmlused for the purposes of this example
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
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
Trang 8contactsDataSet.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>”);
}}
Similar to loading the DataSetwith the schema of the database table, you can also load a DataTablewith the schema of the database table and then load that DataTablewith XML data from an externalXML 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 DataSetschema information (and
no data) from an XML document This method loads a DataSetschema using the XSD schema TheReadXmlSchema()method takes a stream, an XmlReader, or a file name as a parameter In the event ofabsence of an inline schema in the XML document, the ReadXmlSchema()method interprets the schema
221
Trang 9from the elements in the XML document When you use the ReadXmlSchema()method to load a DataSetthat 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()methodthrows an exception if the types of the column in the DataSetand the column in the XML document areincompatible.
The InferXmlSchema Method
You can also use the InferXmlSchema()method to load the DataSetschema from an XML document.This method has the same functionality as that of the ReadXml()method that uses the XmlReadModeenumeration value set to InferSchema The InferXmlSchema()method, besides enabling you to inferthe schema from an XML document, enables you to specify the namespaces to be ignored when inferringthe schema This method takes two parameters The first parameter is an XML document location, astream, or an XmlReader; the second parameter is a string array of the namespaces that need to beignored when inferring the schema
Transforming DataSet to XML
You can easily fill a DataSetwith data from an XML stream or document The information suppliedfrom the XML stream or document can be combined with existing data or schema information that isalready present in the DataSet On the other side, you can use the WriteXml()method of the DataSetobject to serialize the XML representation of the DataSetto a file, a stream, an XmlWriterobject, or astring While serializing the contents, you can optionally include the schema information
To control the actual behavior of the WriteXml()method, you set the XmlWriteModeenumeration to any of the values shown in Table 8-2 The values supplied to the XmlWriteModeenum determine the layout of the XML output The DataSetrepresentation includes tables, relations, and constraints definitions.The rows in the DataSet’stables are written in their current versions unless you choose to employ theDiffGramformat Table 8-2 summarizes the writing options available with XmlWriteMode
Table 8-2 Members of XmlWriteMode Enumeration
DiffGram Allows you to write the entire DataSetas a DiffGram, including original
and current valuesIgnoreSchema Writes the contents of the DataSetas XML data, without an XSD schemaWriteSchema Writes the contents of the DataSetas 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 DataSetacross 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
Trang 10As you can see, ADO.NET allows you to write XML data with or without the XML schema When youwrite DataSetdata as XML data, the current version of the DataSetrows is written; however,ADO.NET enables you to write the DataSetdata as a DiffGram, which means that both original aswell as current versions of the rows would be included Before proceeding further with an example, it isimportant to understand what DiffGramsare.
DiffGrams
ADiffGramis in XML format and is used by the DataSetto store the contents ADiffGramis used
to discriminate between the original and current versions of data When you write a DataSetas aDiffGram, the DiffGramis populated with all information that is required to re-create the contents ofthe dataset These contents include the current and original values of the rows and the error informationand order of the rows; however, the DiffGramformat doesn’t get populated with the information to re-create the XML schema ADataSetalso uses the DiffGramformat to serialize data for transmissionacross the network
The DiffGramformat consists of the following data blocks:
❑ <DataInstance>represents a row of the DataTableobject or a dataset and contains the currentversion 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>tion in the current data section Now that you have the basic knowledge of DiffGram, you will learn howyou can write a dataset as XML data If you want to write the XML representation of the DataSetto anXmlWriter, a stream, or a file, you need to use the WriteXml()method The WriteXml()method takestwo parameters The first parameter is mandatory and is used to specify the destination of XML output Thesecond parameter is optional and is used to specify how the XML output would be written The secondparameter of the WriteXml()method is the XmlWriteModeenumeration to which you can pass in any ofthe values shown in Table 8-2
annota-Listing 8-4 shows you an example on how to serialize the DataSetto an XML file using the WriteXml()method of the DataSet It also shows how easily you can cache the contents of the DataSetin a localXML file that obviates the need to retrieve the data from the database every time
Listing 8-4: Serializing DataSet to XML Using WriteXml
Trang 11void 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 DataSetcontactsDataSet.ReadXml(filePath);
}else{contactsDataSet = GetContactTypes();
//Write the contents of the DataSet to a local XML filecontactsDataSet.WriteXml(filePath);
}gridContacts.DataSource = contactsDataSet.Tables[0].DefaultView;
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;
<asp:BoundField HeaderText=”Contact Type ID” DataField=”ContactTypeID”/>
<asp:BoundField HeaderText=”Name” DataField=”Name”
Trang 12The Page_Load event shown in Listing 8-4 starts by checking for the presence of an XML file namedContactType.xml If the file is not present, it invokes a private helper method namedGetContactTypes()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 DataSetinto a local XML file named ContactType.xml Duringsubsequent requests, this local XML file is used to display the contact types information Finally, the con-tents of the DataSetare displayed through a GridView control
Controlling the Rendering Behavior of DataColumn Objects
When you serialize a DataSetobject to XML, the contents in the DataSetare rendered either as elements
or attributes It is possible to control this rendering behavior by setting appropriate attributes at theDataColumnobject level The DataColumnobject has a property called ColumnMappingthat determineshow columns are rendered in XML The ColumnMappingproperty takes values from the MappingTypeenum Table 8-3 summarizes the various values supported by the MappingTypeenum
Table 8-3 Members of MappingType Enumeration
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 MappingTypeenumeration by setting theColumnMappingproperty for all the columns in the ContactTypetable 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 whenthe DataSetis serialized to XML format
Modifying the Table and Column Names
By default, when you use the DataAdapterto fill a DataSet, the column names that are used in theDataSetcorrespond 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 aretimes where the default XML elements may not work for you and you might need the ability to tailor theXML elements One way of solving this problem is to use some sort of mapping With ADO.NET, thereare two places that you can implement mapping: at the query level, or in the DataAdapter
225
Trang 13The SQL language provides a basic ability to change column names using the AS keyword For example,the following query selects three columns from the ContactTypetable, 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 orstored procedure in the database If column names change, you can simply update the correspondingview 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 mappingtechnique in ADO.NET The basic principle is to apply a list of column transformations to the
DataAdapterobject When you fill a DataSetusing the DataAdapter, it automatically renames thesource 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
“ContactType” is mapped to the DataTablenamed “ContactType” — in other words, the name is notchanged However, this step is still required If no parameter is specified, the default name (“Table”) isused Note that this technique is useful if you are directly filling a DataTableand serializing it to XML
Of course, nothing prevents you from using table mappings, if you want In the following example, theFill()method actually creates a table in the DataSetnamed 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 returnsmultiple result sets In this case, a number is automatically added to the table name for each subsequentresult set, as in ContactType, ContactType1, ContactType2, and so on By adding a DataTableMappingobject 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;
Trang 14The DataAdapterprovides a special MissingMappingActionproperty that governs how it behaves ifyou do not supply column and table mappings It takes one of the MissingMappingActionvaluesdescribed in Table 8-4.
Table 8-4 Values of MissingMappingAction Enumeration
Error If there is any column that doesn’t have a mapping, an exception is
thrownIgnore All columns that do not have a mapping are ignored, and not added
to the DataSetPassthrough 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 theMissingMappingActionproperty 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 DataSetinto a string for use in your code Although it isnot a recommended technique for extracting XML from a DataSet, it can be useful in situations whereyou want to use the DataSetobject as a string in your code To accomplish this, use the GetXml()andGetXmlSchema()methods of the DataSetobject Listing 8-5 shows an example of how to retrieve XMLdata from a DataSetdirectly using GetXml()and GetXmlSchema()methods
Listing 8-5: Retrieving XML as a String from a DataSet
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 controlsltlXmlData.Text = Server.HtmlEncode(contactsDataSet.GetXml());
ltlXmlSchema.Text = Server.HtmlEncode(contactsDataSet.GetXmlSchema());
}
</script>
227
Trang 15Nesting XML Output from a DataSet
As you know, a DataSetcan contain more than one table and also the relationships between thesetables In an ADO.NET dataset, DataRelationis used to implement the relationship between tablesand work with the child rows from one table that are related to a specific row in the parent table XMLprovides a hierarchical representation of data in which the parent entities contain nested child entities.The DataRelationobject has a property named Nested This property is false by default and has noeffect when you are accessing the data using relational techniques It does, however, affect the way thedata is exported as XML when you save the contents of the DataSetusing WriteXml()method
The code shown in Listing 8-6 demonstrates how to create nested XML output from a DataSetusing anXmlDataDocumentobject
Listing 8-6: Generating Nested XML Output from a DataSet
DataRelationrelates two DataTableobjects by using DataColumnobjects These
relationships are created between the matching records in the parent and child
tables The DataTypevalue of these columns should be identical The referential
integrity between the tables is maintained by adding the ForeignKeyConstraintto
ConstraintCollectionof the DataTableobject Before a DataRelationis created,
it first checks whether a relationship can be established If a relationship can be
estab-lished, a DataRelationis created and then added to the DataRelationCollection.
You can then access the DataRelationobjects from the DataRelationCollectionby
using the Relationsproperty of the dataset and the ChildRelationsand
ParentRelationsproperties of the DataTableobject.
228
Trang 16<script runat=”server”>
void Page_Load(Object sender, EventArgs e){
string filePath = Server.MapPath(“NestedOutput.xml”);
//Get the values from the databaseDataSet prodCategoriesDataSet = GetCategoriesAndProducts();
XmlDataDocument xmlDoc = new XmlDataDocument(prodCategoriesDataSet);
//Write the datasetxmlDoc.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);
prodCategoriesDataSet.Relations[0].Nested = true;
} return prodCategoriesDataSet;
<asp:HyperLink ID=”hlkDataSetOutput” Runat=”server”>
Click here to view the output of the Serialized DataSet Output
Trang 17Opening 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 DataSetenables you to write a DataSetschema through the WriteXmlSchema()method, facilitatingthe transportation of this schema in an XML document This method takes one parameter, which specifiesthe location of the resulting XSD schema, and the location can be a file, an XmlWriter, or a Stream
Typed DataSets
A typed DataSetis a subclass of DataSetclass in which the tables that exist in the DataSetare derived byreading the XSD schema information The difference between a typed DataSetand an ordinary DataSetisthat the DataRows, DataTables, and other items are available as strong types; that is, rather than refer toMyDataSet.Tables[0]or MyDataSet.Tables[“customers”], you code against a strongly typedDataTablenamed, for example, MyDataSet.Customers Typed DataSetshave the advantage that thestrongly typed variable names can be checked at compile time rather than causing errors at runtime 230
Trang 18Suppose you have a DataSetthat contains a table named customers, which has a column namedCompanyName 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 compilercannot 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 DataSetitemswere strongly typed, misspelling the column name or using the wrong ordinal would be prevented becausethe code simply would not compile The following code shows you how to assign the same value to thesame 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 DataSetclass When you create a typedDataSet, you are creating a class that implements the tables and fields based upon the schema used togenerate the class Basically, the schema is coded into the class
As you compare the two examples, you see that a typed DataSetis easier to read and understand It isless error-prone, and errors are realized at compile time as opposed to runtime
Generating Typed DataSets
The easiest way to generate a typed DataSetis to use Visual Studio In Visual Studio, just right-click
on the existing table, stored procedure, SQL statement and select “Generate DataSet.” The NETFramework SDK also ships with a tool called XSD.exe that provides you with more control and optionsfor generating the XML schema
Instead of using Visual Studio, you also have the option of generating the typed DataSetmanuallyusing the following steps
1. Fill a DataSetwith data from the database
2. Save the schema with DataSet.WriteXmlSchema()
3. Use the schema as input into XSD.exe
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.XmlSerializercompatible types, meaning that it is look- ing for types with public fields, or public properties with both getter and setters.
Trang 19For example, generate a typed DataSetfor 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.xsdfile generated, you can then utilize the XSD.exeutility to generatethe typed DataSet
xsd /DataSet /language:CS C:\Data\Products.xsd
The previous command in the command prompt creates a typed DataSetcalled ProductsDataSetinthe Products.csfile 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.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
Trang 20-public partial class ProductsTableRow : System.Data.DataRow {private ProductsTableDataTable tableProductsTable;
- - } public class ProductsTableRowChangeEvent : System.EventArgs {private ProductsTableRow eventRow;
- }
-}The typed DataSetaccomplishes strong typing by generating a class ProductsDataSet, which derivesfrom DataSetclass The name of the subclass of the DataSetclass is equal to DataSet.DataSetName
in the original DataSetthat produced the XML schema As you can see from the previous code listing,four public nested classes are exposed:
❑ The PrimaryKeyproperty is added to DataColumnproperties for the parent table
❑ AForeignKeyConstraintis added for the child table
❑ DataRelationis added
If the DataSet’s Nestedproperty was set in the original schema, it is preserved in the typed DataSet
Using Typed DataSet
In most cases, you can use your Typed DataSeteverywhere you would normally use a DataSet YourTyped DataSetdirectly derives from DataSetso all the DataSet-related classes and methods willwork One of the distinct advantages of using a Typed DataSetis that elements of the DataSetarestrongly typed and strongly named The use of the new class is a little different than our old DataSetexamples, as follows:
ProductsDataSet prodDS = new ProductsDataSet();
SqlDataAdapter adapter = new SqlDataAdapter(“SELECT * FROM Production.Product “ +
Trang 21When filling the typed DataSetwith data, you can expect the same behavior as that of the DataSetbecausethe typed DataSetdirectly derives from the base DataSetclass 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 untypedDataSet
Using Annotations with a Typed DataSet
Although strongly typed DataSetsare produced using the names in the schema, you can refine thenaming 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 DataSetwhen the value in the underlying table is DBNull
For example, consider the XSD schema
The previous schema element for the ProductsTablethat represents the Products table of the
AdventureWorks database would result in a DataRowname of ProductsTableRow In common ios, the object would be referred to without the Row identifier and instead would be simply referred to
scenar-as a Product object Similarly, you would want to refer to the collection of Products simply scenar-as Products
To accomplish this, you need to annotate the schema and identify new names for the DataRowandDataRowCollectionobjects The following code shows the annotated schema that will produce thedesired output
<xs:element name=”ProductsTable” codegen:typedName=”Product”
234
Trang 22The following code shows how you can loop through the DataSetusing the new DataRowandDataRowCollectionobject names.
ProductsDataSet prodDS = new ProductsDataSet();
SqlDataAdapter adapter = new SqlDataAdapter(“SELECT * FROM Production.Product “ +
XmlDataDocument Object and DataSet
The key XML class that makes it possible to access both relational and hierarchical data in a consistentmanner is the XmlDataDocumentclass The XmlDataDocumentclass inherits from the base classXmlDocumentand differs from it only in the ability to synchronize with DataSetobjects When synchro-nized, DataSetand XmlDataDocumentclasses work on the same collection of rows, and you can applychanges through both interfaces (nodes and relational tables) and make them immediately visible toboth classes Basically, DataSetand XmlDataDocumentprovide two sets of tools for the same data As aresult, you can apply XSLT transformations to relational data, query relational data through XPathexpressions, and use SQL to select XML nodes It can also be useful in situations when you want toretain the full fidelity of fairly unstructured XML and still use methods provided by the DataSetobject.Because the XmlDataDocumentclass is derived from the XmlDocumentclass, it supports all the proper-ties and methods of the XmlDocumentclass Additionally, the XmlDataDocumentclass has its own prop-erties and methods for providing a relational view of the data contained in it Table 8-5 discusses theproperties and methods of the XmlDataDocumentin this context
Table 8-5 Important Properties and Methods of the XmlDataDocument Class
Property or Method Description
DataSet This property returns reference to the DataSetthat provides a
relational representation of the data in the XmlDataDocumentGetElementFromRow This method retrieves the XmlElementassociated with the specified
DataRowGetRowFromElement This method returns reference to the DataRowassociated with the
specified XmlElementLoad This method loads the XmlDataDocumentusing the specified data
source and synchronizes the DataSetwith the loaded data
235
Trang 23Data can be loaded into an XmlDataDocumentthrough either the DataSetinterfaces or the
XmlDocumentinterfaces You can import the relational part of the XML document into DataSetbyusing an explicit or implied mapping schema Whether changes are made through DataSetor throughXmlDataDocument, the changed values are reflected in both objects The full-fidelity XML is alwaysavailable through the XmlDataDocument
Associating an XmlDataDocument with a DataSet
There are a few ways to bind a DataSetobject and XmlDataDocumentobject together The first option isthat you pass a non-empty DataSetobject to the constructor of the XmlDataDocumentclass as follows.XmlDataDocument xmlDoc = new XmlDataDocument(dataset);
Similar to its base class, XmlDataDocumentprovides a XML DOM approach to work with XML data Analternate way of synchronizing the two objects is by creating a valid and non-empty DataSetobjectfrom a non-empty instance of the XmlDataDocumentobject An example of this is illustrated here.XmlDataDocument xmlDoc = new XmlDataDocument();
xmlDoc.Load(filePath);
DataSet dataset = xmlDoc.DataSet;
You can turn an XmlDataDocumentinto a DataSetobject using the XmlDataDocument’s DataSetproperty The property instantiates, populates, and returns a DataSetobject The DataSetis associatedwith the XmlDataDocumentthe first time you access the DataSetproperty To view the XML data rela-tionally, you must first specify a schema to use for data mapping This can be done by calling theReadXmlSchema()method on the same XML file As an alternate approach, you can manually createthe necessary tables and columns in the DataSet
Keeping the two objects synchronized provides an unprecedented level of flexibility by allowing you touse two radically different types of navigation to move through records In fact, you can use SQL-likequeries on XML nodes, as well as XPath queries on relational rows Keep in mind, however, that not allXML files can be successfully synchronized with a DataSet For this to happen, XML documents musthave a regular, tabular structure that can be mapped to a relational architecture where each row has thesame number of columns Also remember that when rendered as DataSetobjects, XML documents loseany XML-specific information they may have and for which there isn’t a relational counterpart Thisinformation includes comments, declarations, and processing instructions
Loading a DataSet through an XmlDataDocument
You can load a DataSetthrough either the DataSetinterfaces or the XmlDataDocumentinterfaces Forexample, you can import the relational part of the XML document into DataSetby using an explicit orimplied mapping schema One of the important advantages of this approach is that whether the changesare made through DataSetor through XmlDataDocument, the changed values are reflected in bothobjects Moreover, you can obtain the full fidelity XML any time through the XmlDataDocument Listing8-8 illustrates the code required for loading a DataSetthrough 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
Trang 24string 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 objectgridContacts.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);
Trang 25In the Page_Loadevent, an XmlDataDocumentobject is instantiated After that, the ReadXmlSchema()method of the DataSetobject is used to load the XML schema After the schema is loaded, the actualXML data is then loaded using the Load()method of XmlDataDocumentobject Now that the schema aswell as the XML data is loaded into the XmlDataDocumentobject, you can now retrieve the DataSetusing the DataSetproperty of the XmlDataDocumentobject Finally, this DataSetobject is bound to aGridViewcontrol Navigate to the page using the browser, and you should see an output very similar toFigure 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 DataSetobject as an XML element To accomplish this, perform the following steps
1. Create a DataSetobject and fill it with the values from a database table
2. Associate an XmlDataDocumentobject with the DataSetby passing in the DataSetobject tothe constructor of the XmlDataDocumentobject
3. Invoke the GetElementFromRow()method of the XmlDataDocumentobject and pass in theDataRowobject as an argument The GetElementFromRow()method returns an XML represen-tation of the DataRowobject in the form of an XmlElementobject
Listing 8-9 shows an example of how to extract XML data using the GetElementFromRow()method ofthe XmlDataDocumentobject
Listing 8-9: Extracting XML Elements from an XmlDataDocument Object
Trang 26<script runat=”server”>
void Page_Load(Object sender, EventArgs e){
DataSet contactTypesDataSet = GetContactTypes();
//Associate the DataSet with an XmlDataDocument objectXmlDataDocument xmlDoc = new XmlDataDocument(contactTypesDataSet);
//Loop through the DataTable and retrieve the XML DataDataTable table = xmlDoc.DataSet.Tables[0];
StringBuilder builder = new StringBuilder();
foreach (DataRow row in table.Rows){
builder.Append(xmlDoc.GetElementFromRow(row).OuterXml);
}ltlXMLData.Text = Server.HtmlEncode(builder.ToString());
}DataSet GetContactTypes(){
string connString = WebConfigurationManager
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select * from Person.ContactType”;
DataSet contactTypesDataSet = new DataSet(“ContactTypes”);
using (SqlConnection sqlConn = new SqlConnection(connString)){
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactTypesDataSet);
}return contactTypesDataSet;
239
Trang 27Similar to the GetElementFromRow()method of the DataSet, there is also a mirror method named
GetRowFromElement()of the XmlDataDocumentobject that exactly does the opposite As the
name suggests, this method takes in an XmlElementobject as its parameter and returns a DataRowas its output This method is useful in situations when you want to extract field information about the ele- ments contained in the XmlElementobject.
Merging Data with XmlDataDocument
There are times when you may want to merge data from a relational database into an existing
XmlDataDocumentobject You can easily accomplish this by using the Merge()method of the DataSet.When you merge data into an XmlDataDocument’s DataSet, you need to ensure that the schema forboth tables must be loaded when the original document is loaded; otherwise, this will result in an excep-tion during DataSet.Merge()
Listing 8-10: Merging Data in an XmlDataDocument with DataSet
DataSet contactTypesDataSet = GetContactTypes();
XmlDataDocument xmlDoc = new XmlDataDocument();
DataSet contactTypesDataSet = new DataSet(“ContactTypes”);
using (SqlConnection sqlConn = new SqlConnection(connString)){
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactTypesDataSet);
}return contactTypesDataSet;
Trang 28<title>Merging a DataSet with an XmlDataDocument Object</title>
Figure 8-4
241
Trang 29Relationship between XmlDataDocument
and XPathNavigator
An additional advantage of using XmlDataDocumentis that you can query the resulting object modelusing either the SQL-like syntax of DataViewfilters or the XPath query language Using DataView
filters, you can only filter sets of rows But using a full-featured query language like XPath you can
produce sets of nodes or scalar values that can be easily iterated through You can use XPath directly via theSelectSingleNodeand SelectNodemethods that XmlDataDocumentinherits from XmlDocument TheXPathNavigatorclass also lets you use precompiled XPath queries Result sets from XPath queries areexposed as XPathNodeIteratorobjects that provide you with a mechanism for iterating over a set ofselected nodes You can also use XPathNavigatoras an input to the XSLT transformation process exposedthrough the XslCompiledTransformclass Listing 8-11 demonstrates the use of XPathNavigatorobject byshowing an example
Listing 8-11: Navigating the Contents of an XmlDataDocument Using an
DataSet contactTypesDataSet = GetContactTypes();
XmlDataDocument xmlDoc = new XmlDataDocument(contactTypesDataSet);
XPathNavigator navigator = xmlDoc.CreateNavigator();
XPathNodeIterator iterator = navigator
Select(“/ContactTypes/ContactType/Name”);
Response.Write(“The retrieved Contact Type Names are : <br>”);
while (iterator.MoveNext()){
Response.Write(iterator.Current.Value + “<br>”);
}}
DataSet GetContactTypes()
{
string connString = WebConfigurationManager
ConnectionStrings[“adventureWorks”].ConnectionString;
string sql = “Select * from Person.ContactType”;
DataSet contactTypesDataSet = new DataSet(“ContactTypes”);
using (SqlConnection sqlConn = new SqlConnection(connString)){
SqlDataAdapter adapter = new SqlDataAdapter(sql, sqlConn);
adapter.Fill(contactTypesDataSet, “ContactType”);
}return contactTypesDataSet;
Trang 30<title>Navigating an XmlDataDocument through XPathNodeIterator</title>
DataTable and XML
Now with ADO.NET 2.0, the DataTableis now a first class citizen and supports serialization alongwith other DataSetfeatures It is because of the fact the DataTableclass now implements theIXmlSerializableinterface In addition, the DataTableclass now supports the ReadXml/WriteXmlmethods that could only be emulated in NET Framework 1.x Table 8-6 outlines the important XMLrelated methods supported by the DataTableclass
Table 8-6 DataTable’s XML Methods
ReadXml Reads XML and data into the DataTablefrom sources such as a
Stream, XmlWriter, or a TextWriterReadXmlSchema Reads an XML schema into the DataTablefrom a variety of sources
like a Stream, XmlWriter, or a TextWriterWriteXml Allows you to write the current contents of the DataTableto a File,
Stream, TextWriter, or an XmlWriterobjectWriteXmlSchema Allows you to write the schema of the DataTableobject
Now that you have a general understanding of the XML related methods of the DataTable, Listing 8-12shows you a code example that exercises some of these methods
Note that Listing 8-11 uses the XmlDataDocumentfor loading the XML document into the memory Because the XmlDataDocumentis not optimized for XPath queries, this approach is not recommended and might result in negative performance impact
to your application.
243