XML and Strongly Typed Datasets |
by Paul Glavich |
XML and the schemas associated with describing the XML, play an important role within .Net and Microsofts vision overall. This article will detail the usage, with some tips and tricks for using Strongly Typed datasets from an XML Schema that can be utilised by a .Net developer. |
|
What is a Strongly typed dataset? Glad you asked.... |
A Dataset is an extremely flexibile method of accessing and manipulating data. A standard dataset provides a "late bound", collection based method of accessing and updating data. The type of data stored within the dataset is unknown at compile time and requires explicit casting and dynamic resolution at run-time. This is referred to as "weak typing". The term "Strongly Typed" refers to the explicit naming and typing of methods and member variables of a class (in this case a dataset), as opposed to the generic methods of accessing and manipulating the class data, such as using collection classes. The type of the data is known at compile time and as such, allows incorrect data type assignments to be resolved at compile-time rather than run-time, saving processing and decreasing the chance of errors in your code. In addition, the "intellisense" features of the IDE (such as in Visual Studio.NET) can be effectively used to provide meaningfull member variable descriptions at develop time to ease coding. The C# code example below best illustrates this:- |
The code below access the 'CustomerID' field in the 'Customers' table in a weakly typed dataset:- |
|
string s = (string) myDataSet.Tables["Customers"].Rows[0]["CustomerID"]; |
The code below access the 'CustomerID' field in the 'Customers' table in a strongly typed dataset:- |
|
string s = myDataSet.Customers[0].CustomerID; |
As you can see, the strongly typed method of accessing data within a dataset is not only more intuitive and easier to read, there is no explicit casting involved to cast the row value to a specific type (in this case a string). In a weakly typed dataset, the dataset does not know what kind of data will be returned until run-time. In a strongly typed dataset, the member variable is of an explicit type (in this case string), and no casting is required. Trying to assign a value to a member variable of anything but the proper type in a strongly typed dataset, will result in a compile error, thus minimising errors at run-time. |
|
How can we do this using .Net and why? |
Imagine designing your XML data structure and then having .Net generate a class automatically for you, to access and manipulate this data in an intuitive and easy way, without any coding. This is the strongly typed dataset generation feature. You can design the XML Schema that your application requires, and have .Net generate a strongly typed dataset to access and manipulate data in this format explicitly. Using the strongly typed paradigm, class members will be explicitly named and typed according to the corresponding elements in the XML Schema. This saves a huge amount of time writing data access classes to manage data according to your defined schema. This can be accomplished in 2 ways. If you dont have Visual Studio.NET, the .Net SDK comes with a command lines tool 'XSD.EXE' which can be used to generate your class file. Its syntax (in its most basic form) is as follows:- |
|
XSD {your_schema}.xsd /d |
This will generate a class file named "{your_schema}.cs". Note: To generate a class in VB.NET, use the "/l" language switch as follows:- |
|
XSD {your_schema}.xsd /d /l:VB |
This will generate a class file named "{your_schema}.vb". |
If you are using Visual Studio.NET, things are even easier. Add an XML Schema to your project and design your schema as you see fit, then on the designer surface of the XML Schema, right click your mouse, and select "Generate Dataset". Alternatively, select the "Schema" menu and select "Generate Dataset". This will generate a strongly typed dataset and add it to your project automatically. This option will remain set in Visual Studio.NET (unless you turn it off), so that any changes to the schema will automatically regenerate the dataset class. |
|
You now have a strongly typed dataset to manipulate data according to your XML Schema without any coding. Magic stuff which can save a developer many hours of coding such classes by hand, particularly when you have many schemas. This also has the benefit of automatically serialising itself in the schema format when required and providing the familiar dataset programming paradigm when manipulating the data. |
|
Usage |
At its simplest level, a dataset, like a database, has rows of information, and this is how we add information to the dataset. With a standard dataset, we would be dealing with a standard 'Datarow' object, but with strongly typed datasets, we are dealing with rows of an explicit type, depending on the schema definition. Typically, we create a 'Datarow' object, set the relevent column properties, and add that row to the dataset. An example best illustrates this. |
Lets say we have a schema like the one shown below:- |
|
<?xml version="1.0" encoding="utf-8" ?>
<xs:schema id="ExampleSchema"
targetNamespace="http://tempuri.org/ExampleSchema.xsd"
elementFormDefault="qualified"
xmlns="http://tempuri.org/ExampleSchema.xsd"
xmlns:mstns="http://tempuri.org/ExampleSchema.xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType name="ctSubNode">
<xs:sequence>
<xs:element name="subNode_element1" type="xs:string" />
<xs:element name="subNode_AnotherElement" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="mainNode">
<xs:complexType>
<xs:sequence>
<xs:element name="myNode_element" type="xs:string" />
<xs:element name="sub_Node" type="ctSubNode" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
|
which would be used to validate an XML instance document as shown below:- |
|
<ExampleSchema xmlns="http://tempuri.org/ExampleSchema.xsd">
<mainNode>
<myNode_element>mainnode element value</myNode_element>
<sub_Node>
<subNode_element1>sub node 1 element value</subNode_element1>
<subNode_AnotherElement>sub node 1 Anotherelement value</subNode_AnotherElement>
</sub_Node>
</mainNode>
</ExampleSchema>
|
Using the schema above, we can generate a strongly typed dataset. Using the command line and assuming you have a file named "ExampleSchema.xsd" that continas the schema above, type:- |
|
'XSD ExampleSchema.xsd /d' |
which will generate a class file 'ExampleSchema.cs'. Alternatively, right click on the designer surface in Visual Studio.NEt and ensure 'Generate Dataset' is enabled. |
Now within our main code, we can utilise this strongly typed dataset as shown in the following code (C#) below:- |
|
// Create our ExampleSchema object from the generated class
ExampleSchema exampleDS = new StrongTypeDatasetDemo1.ExampleSchema();
// Add a new row to the mainNode element.
// Note: We could have created a distinct 'row' object (as shown below)
// to assign values to, then add the row. This method below does
// it all in one step.
exampleDS.mainNode.AddmainNodeRow("mainnode element value");
// Create a new 'sub_Node' row object
ExampleSchema.sub_NodeRow sub_node_row = exampleDS.sub_Node.Newsub_NodeRow();
// Assign some values to the row.
sub_node_row.subNode_element1 = "sub node element value";
sub_node_row.subNode_AnotherElement = "sub node 1 Anotherelement value";
// Add the row to the XML Document (Instance document of the ExampleSchema)
exampleDS.sub_Node.Addsub_NodeRow(sub_node_row);
// Accept the changes
exampleDS.AcceptChanges();
|
This all seems easy enough, but there is a problem. The code shown above will produce the following XML instance document:- |
|
<ExampleSchema xmlns="http://tempuri.org/ExampleSchema.xsd">
<mainNode>
<myNode_element>mainnode element value</myNode_element>
</mainNode>
<sub_Node>
<subNode_element1>sub node element value</subNode_element1>
<subNode_AnotherElement>sub node 1 Anotherelement value</subNode_AnotherElement>
</sub_Node>
</ExampleSchema>
|
This differs from the initial XML document that was described earlier, such that all nodes are at the same level. The "sub_Node" node is not nested within the "mainNode" node. This is clearly not what was intended and yet this was generated from our strongly typed dataset. The key to getting the XML instance document how we want it is the parent/child relationship. This relationship between the "mainNode" node and the "sub_Node" node is NOT explicitly generated and catered for by the strongly typed dataset, and so must be explicitly set in our code. The C# code shown below shows a modification to our original code to produce an XML instance document that conforms to our schema:- |
|
// Create our ExampleSchema object from the generated class
ExampleSchema exampleDS = new StrongTypeDatasetDemo1.ExampleSchema();
// Add a new row to the mainNode element.
// Note: We DID NOT simply call the AddmainNodeRow methiod as above as
// we want to reference this row as a 'parent' row later in the
// code.
ExampleSchema.mainNodeRow main_node_row = exampleDS.mainNode.NewmainNodeRow();
main_node_row.myNode_element = "mainnode element value";
exampleDS.mainNode.AddmainNodeRow(main_node_row);
// Create a new 'sub_Node' row object
ExampleSchema.sub_NodeRow sub_node_row = exampleDS.sub_Node.Newsub_NodeRow();
// Assign some values to the row.
sub_node_row.subNode_element1 = "sub node 1 element value";
sub_node_row.subNode_AnotherElement = "sub node 1 Anotherelement value";
// !!IMPORTANT!! This is where we 'define' the parent relationship
// to the 'mainNodeRow' created above. Wihout this line
// below, no 'heirarchy' will be maintained as expected.
sub_node_row.mainNodeRow = main_node_row;
// Add the row to the XML Document (Instance document of the ExampleSchema)
exampleDS.sub_Node.Addsub_NodeRow(sub_node_row);
// Accept the changes
exampleDS.AcceptChanges();
|
Notice the line : |
sub_node_row.mainNodeRow = main_node_row; |
which is the key to defining the parent relationship of the "mainNode" node to the "sub_Node" node. Now the correct XML is produced. Without this line, if your XML schemas contain nested elements (as many do), your XML files will come out looking "flat". A link to a Visual Studio.NET project containing the sample code shown above, with schemas and WinForm application demonstrating the above concepts can be found here. |
|
Conclusion. |
This represents only the tip of the iceberg when it comes to strongly typed datasets. While their usage is easy, its important to be aware of some key concepts so that you can make correct use and take full advantage of them. This article provides a brief glimpse of what strongly typed datasets are, their advantages, and how they can be used correctly. I urge you to take a small amount of time to investigate the usage of these powerfull components in your applications. While they certainly dont apply to every situation, they can save an enormous amount of time and effort wherever they are used. |
Download the Demo Code |
|
Look out for future articles in this series coming soon... |
|
If you have any questions, please feel free to email me, Paul Glavich |
|
Thanks to the following people who helped me in various ways for this article:- |
Zubin Appoo David Chandra Rob Brunet
|