Reproduced with kind permission of Wrox Press |
Chapter 12: ADO.NET and XML |
ISBN: |
1861005555
|
Author(s): |
Matthew Reynolds, Denise Gosnell, Bill Forgey |
What does this book cover?
- The basic principles of relational database design
- Microsoft SQL Server Desktop Engine
- Querying the database with the T-SQL language
- Visual Studio .NET and the Server Explorer
- ADO.NET and the DataSet
- Data binding, updating the database, and conflict resolution
- XML's role in ADO.NET
- Accessing data with ASP.NET and Web Services
|
|
Page 3 of 7
Go to page: 1 2 3 4 5 6 7
|
12
ADO.NET and XML
|
Schemas
One issue with XML is that, as we said before, it's a set of rules for defining markup languages. It is extensible we can define our own elements. However, this also means that we need some way of understanding everyone else's elements, which can look completely different to our own. So we need to be able to define rules for XML documents that help them become as self-describing as possible. We need rules which let anyone reading the XML document understand that, for instance, "This XML file contains a list of orders as used by the NorthwindSQL database". Schemas provide a mechanism for defining rules that XML documents must adhere to, and which help everyone understand what the data held in an XML document actually is.
A schema defines how different elements can be put together to make a document of a certain type. This is done by using rules; in our XML document, we know the following rules apply:
- Our top-level element is called Orders.
- Our Orders element contains any number of Order elements, from zero to "infinity".
-
Our Order element contains exactly thirteen elements, namely these and in the given order: OrderID, CustomerID, EmployeeID, OrderDate, RequiredDate, ShippedDate (if not Null), ShipVia, Freight, ShipName, ShipAddress, ShipCity, ShipRegion, ShipPostalCode, ShipCountry.
If we provide a schema that defines this structure along with our XML document, everyone will be able to understand the data contained within our document.
As we know, XML is a really useful technology for application integration and what a schema does is allow you to answer the question, "Does this XML file that a business partner gave me contain a list of orders specified in the manner that I am expecting?" If the answer is "Yes", the application can use the data and do something useful. If "No", the application can do something to tell the business partner that something is wrong with the file.
Schemas are a great way of making sure that, once you have a document, you can be confident that the document fits in with your business rules. Imagine you set up a piece of software that receives XML documents through e-mail or some other mechanism (we will look at a situation like this in the Case Study). Each of these documents contains an order from your customers, but you will need those documents to follow certain rules if your application is to be able to use them. For example, the document must contain a customer ID, a delivery address, and each line in the order must specify a product ID, a quantity, and a unit price.
Before you start processing an XML document, it's important to ask whether the document is valid, that is, whether it complies with the structure defined in your schema. We'll look at this concept in more detail later.
For now, the best way to understand schemas is to generate one, so let's do that now.
Try It Out - Generating a Schema |
- If the project is running, close it.
-
Open the code editor for Form1. Add this property:
' SchemaFilename - returns the file used to store a schema...
Public ReadOnly Property SchemaFilename() As String
Get
' Get the app folder...
Dim fileInfo As New FileInfo(Application.ExecutablePath)
Dim folderName As String = fileInfo.DirectoryName
' Return the name...
Return folderName & "\OrdersSchema.xml"
End Get
End Property
|
- Again using the code editor, find the definition for btnConnect_Click. Add this line just after you set the DataSet property:
...
' Set the DataSet property...
Me.DataSet = newDataset |
' Save the schema...
newDataset.WriteXmlSchema(SchemaFilename) |
' Close the database...
connection.Close()
End Sub |
- Run the project and click the Connect button. The two orders will be displayed as normal.
- Find the folder where Visual Studio .NET creates the Order_Export.exe file. This will normally be in the bin folder directly beneath the folder in which you created the project.

- Double-click on the OrdersSchema.xml file. You'll see this:

How It Works
Creating the schema is no problem the DataSet does it for us based on its understanding of the tables that it contains. We do this by calling WriteXmlSchema.
We're not going to learn about schemas in great detail as, by and large, it's easier to get .NET to generate them for us and use them. This, of course, doesn't apply if we've been given a schema to work to by a partner organization. We're only going to provide a brief overview of schemas here but XML is such an important language that you really will benefit from gaining a comprehensive understanding of it. A good place to start is by looking at Beginning XML (Wrox Press, ISBN 1861005598).
The first line of the file tells us that we're looking at an XML document, which also tells us that schemas are actually XML documents in their own right.
<?xml version="1.0" standalone="yes"?>
The next line defines the top-level element for an XML schema, xsd:schema. You'll notice that there are several attributes in this element:
<xsd:schema id="Orders" targetNamespace="" xmlns=""
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:msdata="urn:schemas-microsoft-
com:xml-msdata">
The next element is the xsd:element. This is the first rule we define. In this case, we use the name attribute to define the rule, "The first element in the file will be called Orders." We can now see how the file is becoming self-describing:
<xsd:element name="Orders" msdata:IsDataSet="true" msdata:Locale="en-GB">
The next element is xsd:complextype. This tells us that the Orders element contains other elements:
<xsd:complexType>
This is followed by xsd:choice. These elements are used to add additional rules to the element that we're working with. In this case, we've said the maxOccurs is unbounded, meaning that there's no limit to the number of elements that Orders can contain:
<xsd:choice maxOccurs="unbounded">
Then we have an xsd:element. This creates the rule that "Orders contain Order elements."
<xsd:element name="Order">
Again, we can use xsd:complexType to beef this up to "Orders contain an unbounded number of Order elements."
<xsd:complexType>
xsd:sequence is then used to say that Order elements contain the following set of elements, in order.
<xsd:sequence>
Then we have the block of elements, each one representing a column in the Orders table. The attributes on each xsd:element tag tell us the name of the column, the type of the column, and the minimum times that each one will occur:
<xsd:element name="OrderID" type="xsd:int" minOccurs="0" />
<xsd:element name="CustomerID" type="xsd:string" minOccurs="0" />
<xsd:element name="EmployeeID" type="xsd:int" minOccurs="0" />
You'll notice that we appear to have omitted an end tag for each of the fourteen elements. That's because, if we end a start tag with a forward-slash, we're telling whoever is reading the XML not to expect an end tag as the element contains no data. This is a useful tool for saving space when writing XML files.
Finally, we close all of the elements that we have opened in reverse order:
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
Again, at this introductory level, the way an XML schema file is constructed is common sense and, like we said, it's usually easier to get .NET to create the schemas for us.
Let's look in more detail at why we need schemas.
Checking the Validity of a Document
Earlier in this chapter, we learned that an XML file must be well-formed. (We know that a document is well-formed because .NET will never load a document that isn't.) This is only half the battle because we need to know whether the data contained within fits the format that we require. If the data fits the format, we say that it is valid. Since we can use XML schemas to define rules, it follows that we are able to use XML schemas to determine the validity of an XML file.
Try It Out - Creating a DataSet from a Schema |
- If the project is running, close it.
-
Using the code editor for Form1, find the btnLoad_Click method. Add the highlighted line and comment out the ReadXml call, like this:
Private Sub btnLoad_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnLoad.Click
' Display the dialog...
dlgOpenFile.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*||"
If dlgOpenFile.ShowDialog() = DialogResult.OK Then
' Try and load...
Try
' Create a new DataSet...
Dim newDataset As New DataSet() |
' Load the schema...
newDataset.ReadXmlSchema(SchemaFilename) |
' Load... |
' newDataset.ReadXml(dlgOpenFile.FileName) |
' If we got here, we can load it...
Me.DataSet = newDataset
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
End Sub
|
- Run the project. Click the Load button and open the XML file you saved earlier. You should see this:

How It Works
What I'm trying to demonstrate here is that the schema file contains everything that the DataSet needs in order to generate the table and columns within that table, into which data can be loaded. Calling the ReadXmlSchema method to load the schema, but not calling ReadXml to read the actual data, creates a blank table with all the correct columns but no data.
The principle now is that, when we load the XML file using ReadXml, if the columns as they are defined in the XML file do not match the columns as defined in the schema, we can assume that the file is not valid and therefore we shouldn't try to process the data contained within it.
Checking Validity
Let's try that now.
Try It Out - Checking Validity |
- If the project is running, close it.
-
Using the code editor, find the btnLoad_Click method again. Take the comment out before ReadXml.
...
' Load the schema...
newDataset.ReadXmlSchema(SchemaFilename)
' Load... |
newDataset.ReadXml(dlgOpenFile.FileName) |
' If we got here, we can load it...
Me.DataSet = newDataset
Catch ex As Exception
MsgBox(ex.Message)
End Try
End If
End Sub
|
- Run the project and click Load. You should see the data as normal.

- Find the XML file that we created previously and make a copy of it.
-
Now, open up a copy of Windows Notepad and open the copy of the XML file. Visual Studio.NET contains an XML editor if you'd prefer to use that. Change the name of the Orders start tag to MyOrderList and make the same change to the matching end tag. ( I have shortened the file below for brevity.)
<?xml version="1.0" standalone="yes"?> |
<MyOrdersList> |
<Order>
<OrderID>11077</OrderID>
<CustomerID>RATTC</CustomerID>
...
<ShipCountry>France</ShipCountry>
</Order>
|
</MyOrdersList> |
- To use the schema validation classes in Visual Basic .NET, we need to add a reference to the System.Xml.dll assembly as this contains the classes that we need for schema validation. To do this, stop the application from running and then, using Solution Explorer, right-click on the Order Export project and select Add Reference. This will open the Add Reference dialog box.
- Using the list on the .NET tab. find the System.Xml.dll assembly. Select it, then click the Select button, and then OK.

-
Next, open the code editor for Form1. Find the namespace import declarations at the top of the code listing and add this new one:
Imports System.IO
Imports System.Xml |
Imports System.Xml.Schema |
Imports System.Data.SqlClient
|
-
We need to make some changes to the btnLoad_Click implementation. Remove the ReadXmlSchema call and add this new code:
Private Sub btnLoad_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnLoad.Click
' Display the dialog...
dlgOpenFile.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*||"
If dlgOpenFile.ShowDialog() = DialogResult.OK Then
' Try and load...
Try
' Create a new DataSet...
Dim newDataset As New DataSet()
' Load the schema...
|
newDataset.ReadXmlSchema(SchemaFilename)
' Create a validating reader...
Dim reader As New XmlTextReader(dlgOpenFile.FileName)
Dim validatingReader As New XmlValidatingReader(reader)
' Load our DataSet's schema into the reader...
validatingReader.Schemas.Add(Nothing, SchemaFilename)
' Walk through the document element by element...
Do While True
If validatingReader.Read() = False Then Exit Do
Loop
' Close...
reader.Close()
validatingReader.Close()
|
' Load the document...
newDataset.ReadXml(dlgOpenFile.FileName)
' If we got here, we can load it...
Me.DataSet = newDataset
Catch ex As Exception
|
' What type of exception did we get?
If ex.GetType Is GetType(XmlSchemaException) Then
MsgBox ("The XML file is not valid: " & ex.Message)
Else
MsgBox("A general exception occured: " & ex.Message)
End If |
End Try
End If
End Sub
|
- Now run the project. Click the Load button and open the copy of the XML file, the one that you changed. You should see a message similar to this:

- Click OK and press the Load button again. Open the original XML file and you should see the orders loaded as normal.
How It Works
By changing the copied file, we created a new XML document that didn't fit the rules as defined in the schema. The schema defined a rule that said, "The first element you come across will be called Orders" but, in the file, the first element is called MyOrdersList. The validation properly discoveredthe error and told us about it.
Because reading and validating an XML file takes longer and requires more resources than just reading it, by default the .NET classes that work with XML won't validate against a schema unless we specifically tell them to. To do this, we have to open the file manually using a System.Xml.XmlTextReader object. This class allows us to walk through the document piece-by-piece. We'll show exactly how this works in a little while.
Private Sub btnLoad_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnLoad.Click
' Display the dialog...
dlgOpenFile.Filter = "XML Files (*.xml)|*.xml|All Files (*.*)|*.*||"
If dlgOpenFile.ShowDialog() = DialogResult.OK Then
' Try and load...
Try
' Create a new DataSet...
Dim newDataset As New DataSet()
' Load the schema...
newDataset.ReadXmlSchema(SchemaFilename)
' Create a validating reader...
Dim reader As New XmlTextReader(dialogOpenFile.FileName)
Once we have a reader, we create a System.Xml.XmlValidatingReader object. This type of object has the intelligence to determine if the XML file confirms to a schema that we give it.
Dim validatingReader As New XmlValidatingReader(reader)
When the object is first created, it has no knowledge of any schemas, so we give it one:
' Load our DataSet's schema into the reader...
validatingReader.Schemas.Add(Nothing, SchemaFilename)
This is just half the battle though. We have to walk through the document element by element until something goes wrong. We use the Read method to walk through each node in the document and this method will return False when we reach the end. If something does go wrong, an exception will be thrown and, seeing as we're inside a Try&Catch, we'll know about this later on.
' Walk through the document element by element...
Do While True
If validatingReader.Read() = False Then Exit Do
Loop
Once we've walked through the file we need to close both readers:
' Close...
reader.Close()
validatingReader.Close()
Once we get to this point, we're guaranteed that the document is valid so we can load it into the DataSet and set the DataSet property to display it on the DataGrid:
' Load the document...
newDataset.ReadXml(dlgOpenFile.FileName)
' If we got here, we can load it...
Me.DataSet = newDataset
So, what happens if an exception has been thrown? Well, we're going to get one of two possible types of exception: either a file can't be opened or something else goes wrong; or something specifically related to the validation happens. We can retrieve the System.Type object associated with the exception and choose what to do in each case.
Catch ex As Exception
' What type of exception did we get?
If ex.GetType Is GetType(XmlSchemaException) Then
MsgBox("The XML file is not valid: " & ex.Message)
Else
MsgBox("A general exception occured: " & ex.Message)
End If
End Try
End If
End Sub
Page 3 of 7
Go to page: 1 2 3 4 5 6 7
|
12
ADO.NET and XML
|
|