Typed DataSets


Typed DataSets solve a very specific problem. Before you look at typed DataSets themselves, look at an example of the problem they solve. Examine the schema in Listing 29.3. It is a nested version of the schema that was generated earlier. DataSets are very finicky about the format of the schemas they can read, so the preceding schema has been modified to work with a DataSet.

Listing 29.3. Nested Schema Example
    <?xml version="1.0" standalone="yes"?>    <xs:schema        xmlns=""       xmlns:xs="http://www.w3.org/2001/XMLSchema"       xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">      <xs:element name="Library" msdata:IsDataSet="true">        <xs:complexType>          <xs:choice maxOccurs="unbounded">       <xs:element name="Book">              <xs:complexType>                <xs:attribute name="author" type="xs:string" />                <xs:attribute name="isbn" type="xs:string" use="required" />                <xs:attribute name="title" type="xs:string" />                <xs:attribute name="publishdate" type="xs:string" />                <xs:attribute name="publisher" type="xs:string" />              </xs:complexType>            </xs:element>            <xs:element name="Author">              <xs:complexType>                <xs:sequence>                  <xs:element name="ID" type="xs:string" minOccurs="0" />                  <xs:element name="Name" type="xs:string" minOccurs="0" />                  <xs:element name="Age" type="xs:string" minOccurs="0" />                </xs:sequence>              </xs:complexType>            </xs:element>           <xs:element name="Chapter">              <xs:complexType>                <xs:sequence>                  <xs:element name="ISBN" type="xs:string" minOccurs="0" />                  <xs:element name="Title" type="xs:string" minOccurs="0" />                  <xs:element name="Pages" type="xs:string" minOccurs="0" />                </xs:sequence>              </xs:complexType>            </xs:element>          </xs:choice>        </xs:complexType>        <xs:unique name="PK_BookISBN" msdata:PrimaryKey="true">          <xs:selector xpath=".//Book" />          <xs:field xpath="@isbn" />        </xs:unique>    <xs:keyref name="BookChapters" refer="PK_BookISBN">          <xs:selector xpath=".//Chapter" />          <xs:field xpath="ISBN" />        </xs:keyref>      </xs:element>    </xs:schema> 

This schema still describes the basic data model you saw before. This new schema describes two tables: Book and Chapter . Each book can have multiple chapters, so there is a relationship defined between the Book table and the Chapters table .

Here is some code that reads the schema and then displays some basic information about the DataSet to the console. It also displays the books and their associated chapters:

 using System; using System.Data; namespace DataSetSchema {   class Class1   {     /// <summary>     /// The main entry point for the application.     /// </summary>     [STAThread]     static void Main(string[] args)     {       DataSet ds = new DataSet();       ds.ReadXmlSchema(@"MoreBooks.XSD");       foreach (DataTable table in ds.Tables)       {          Console.WriteLine("Table: {0}\n---------------------", table.TableName);          foreach (DataColumn column in table.Columns)          {            Console.Write("{0} ({1})\t", column.ColumnName, column.DataType.ToString());          }          Console.WriteLine("\n");       }       foreach (DataRelation rel in ds.Relations)       {         Console.WriteLine( "Relation: {0}, Nested: {1}",          rel.RelationName, rel.Nested.ToString() );       }       ds.ReadXml("Morebooks.xml");       Console.WriteLine("{0} Authors Found.", ds.Tables["Author"].Rows.Count);       foreach (DataRow book in ds.Tables["Book"].Rows)       {         Console.WriteLine("Book {0}", book["title"]);         foreach (DataRow chapter in book.GetChildRows("BookChapters"))         {           Console.WriteLine("\tChapter: {0}", chapter["Title"]);         }       }       Console.ReadLine();     }   } } 

The output from running the preceding program is shown in Figure 29.2.

Figure 29.2. Constructing a DataSet from an XSD file.


This shows you that you can create a DataSet structure from an XSD file at runtime. The problem is that this is very inefficient. As you probably noticed, the code is treating each row and column generically, and is doing a lot of type casting and the code doesn't look all that elegant. What's more, we know the structure of our DataSet at compile time, so why should the code wait until runtime to set the structure of the DataSet? It would be great if we could blend the XSD-supplied structure with the DataSet at compile time to create strongly typed wrappers around all the tables and columns. The great thing is that we can.

Creating Typed DataSets in Visual Studio .NET

A typed DataSet is a class that inherits from the DataSet class. It enables you to access data members generically, such as:

 typedDataSet.Tables["MyTable"].Rows[0]["MyColumn"]; 

This access method is what you should be very familiar with as the standard way of getting at data within a DataSet. However, because a typed DataSet creates strongly typed members that are wrappers for the generic access methods, you can actually write code that looks like this:

 typedDataSet.MyTable[0].MyColumn; 

Obviously, this is much easier to read. Even more useful than the ease of reading and maintenance is the fact that each member will already be cast to the appropriate type.

To create a typed DataSet in Visual Studio .NET, right-click any existing project, select Add, and then select Add New Item. When prompted for the type of item to add, select DataSet.

Visual Studio .NET will create a new .xsd file and provide you with an interactive design surface and an XML view of the schema. In addition, a new class will be added to your project, but it won't be visible unless you have the Show All Files option turned on. The chapter doesn't cover the design surface because it focuses on XSD. Enter the XSD from Listing 29.3 in the XML view of the DataSet. You'll see some code for using a typed DataSet shortly. The next section is about using the XSD.EXE command-line tool for generating a typed DataSet.

Building Typed DataSets Using XSD.EXE

The XSD tool is a utility that comes with the .NET Framework SDK to enable you to create XML schemas from existing .NET classes. It also gives you the ability to create typed DataSets from XSD files.

Issue the following command line in the same directory as the Morebooks.XSD file:

 xsd /dataset /namespace:SAMS.SampleDataSet /language:CS Morebooks.xsd 

This command creates a new typed DataSet class called SAMS.SampleDataSet.Library. This chapter will spare you the waste of paper and eyestrain from reading the typed DataSet code. In general, it basically creates members that match each of the columns and tables in the XSD schema for the generated class. The code is extremely lengthy. There are reasons why this class is normally hidden from you by Visual Studio .NET, the biggest of which is the sheer volume of the code in the file. To see this code in Visual Studio .NET, make sure that Show All Files is turned on in this project, and you will be able to see the .CS file below the .XSD schema.

Using Typed DataSets

Thankfully, using a typed DataSet is far easier than reading the code that is used to create one. All you have to do is instantiate it as you would any other ataSet. Instead of using generic DataSet members, you can use the members of the class that were automatically generated by the XSD tool or by Visual Studio .NET.

Listing 29.4 shows the code that instantiates a new library typed DataSet, and uses strongly typed members and methods to display the data and navigating relationships.

Listing 29.4. Code to Manipulate a Typed DataSet
 using System; namespace UsingTypedDataSets {   class Class1   {     /// <summary>     /// The main entry point for the application.     /// </summary>     [STAThread]     static void Main(string[] args)     {       Library library = new Library();       library.ReadXml(@"..\..\..\morebooks.xml");       foreach (Library.BookRow book in library.Book)       {         Console.WriteLine("Book : {0}, Written by {1}",           book.title, book.author );         foreach (Library.ChapterRow chapter in book.GetChapterRows())         {           Console.WriteLine("\tChapter : {0}, Pages {1}",             chapter.Title, chapter.Pages);         }       }       Console.ReadLine();     }   } } 

Annotating Typed DataSets

After looking at the code in Listing 29.4, you should be convinced that using a typed DataSet (if you know the structure of your DataSet at compile/design-time) is beneficial for multiple reasons.

There is a little bit more that can be done. You probably noticed that all the nested classes that represent the strongly typed rows end with the Row postfix. In addition, the name of each column is exactly as it appears in the XML schema, so the title and author attributes also become lowercased column names.

Microsoft provides an additional set of instructions that you can supply in a DataSet's XML schema to allow for annotation. The following list contains the annotations that you can supply to control the definition of the typed DataSet at generation time:

  • typedName The name of this item as it will appear in the DataSet. This annotation is used to override a name so that it doesn't have to match the name in the XML schema.

  • typedPlural Sets the name of a collection of typed objects in the DataSet.

  • typedParent Sets the name of the parent item.

  • typedChildren Gets the name of the method that returns child objects of a given typed row.

  • nullValue If the data row is null, this specifies what value should be stored in the DataSet in place of DBNull.

Being able to annotate an XML schema for DataSet generation requires the addition of the following namespace declaration to the file:

 xmlns:codegen="urn:schemas-microsoft-com:xml-msprop" 

Now look at Listing 29.5, the newly annotated XML schema. The highlighted portions indicate the new annotations .

Listing 29.5. The Annotated Typed DataSet
 <?xml version="1.0" standalone="yes"?> <xs:schema  xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema"      xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"      xmlns:codegen="urn:schemas-microsoft-com:xml-msprop">   <xs:element name="LibraryAnnotated" msdata:IsDataSet="true">     <xs:complexType>       <xs:choice maxOccurs="unbounded">         <xs:element name="Book" codegen:typedName="Book" codegen:typedPlural="Books">           <xs:complexType>             <xs:attribute name="author" type="xs:string" codegen:typedName="Author"/>             <xs:attribute name="isbn"                  type="xs:string" use="required" codegen:typedName="ISBN"/>             <xs:attribute name="title" type="xs:string" codegen:typedName="Title"/>             <xs:attribute                  name="publishdate" type="xs:string" codegen:typedName="PublishDate"/>             <xs:attribute                  name="publisher" type="xs:string" codegen:typedName="Publisher"/>           </xs:complexType>         </xs:element>         <xs:element name="Author" codegen:typedName="Author">           <xs:complexType>             <xs:sequence>               <xs:element name="ID" type="xs:string" minOccurs="0" />               <xs:element name="Name" type="xs:string" minOccurs="0" />               <xs:element name="Age" type="xs:string" minOccurs="0" />             </xs:sequence>           </xs:complexType>         </xs:element>         <xs:element name="Chapter"            codegen:typedName="Chapter" codegen:typedPlural="Chapters">           <xs:complexType>             <xs:sequence>               <xs:element name="ISBN" type="xs:string" minOccurs="0" />               <xs:element name="Title" type="xs:string" minOccurs="0" />               <xs:element name="Pages" type="xs:string" minOccurs="0" />             </xs:sequence>           </xs:complexType>         </xs:element>       </xs:choice>     </xs:complexType>     <xs:unique name="PK_BookISBN" msdata:PrimaryKey="true">       <xs:selector xpath=".//Book" />       <xs:field xpath="@isbn" />     </xs:unique>     <xs:keyref name="BookChapters" refer="PK_BookISBN" codegen:typedParent="Book"                codegen:typedChildren="GetChapters">       <xs:selector xpath=".//Chapter" />       <xs:field xpath="ISBN" />     </xs:keyref>   </xs:element> </xs:schema> With the new annotations, you are now able to rewrite the previous code as follows: LibraryAnnotated libraryA = new LibraryAnnotated(); libraryA.ReadXml(@"..\..\..\morebooks_annotated.xml"); foreach (LibraryAnnotated.Book book in libraryA.Books) {   Console.WriteLine("Book: {0}, Written by {1}", book.Title, book.Author );   foreach (LibraryAnnotated.Chapter chapter in book.GetChapters())   {     Console.WriteLine("\tChapter: {0}, Pages {1}", chapter.Title, chapter.Pages);   } } 



    Visual C#. NET 2003 Unleashed
    Visual C#. NET 2003 Unleashed
    ISBN: 672326760
    EAN: N/A
    Year: 2003
    Pages: 316

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net