Recipe 2.18 Controlling the Names Used in a Strongly Typed DataSet

Recipe 2.18 Controlling the Names Used in a Strongly Typed DataSet

Problem

You want to assign your own names to the classes and properties for strongly typed DataSet classes.

Solution

Use annotations in the XML schema to control the names of classes and properties in strongly typed DataSet classes.

The sample uses one XSD file:

CategoriesDS_AnnotatedName.xsd

The schema used to generate the strongly typed DataSet . The schema is annotated so that you can access the collection of rows in the table by using the Categorys property of the DataSet rather than categories, each row by using the Category property of the row collection rather than CategoriesRow , and the CategoryName field by using the Name property of the row rather than CategoryName . The annotations are marked in bold in the Example 2-23.

Example 2-23. File: TypedDataSets\CategoriesDS_AnnotatedName.xsd
 <?xml version="1.0" standalone="yes" ?>
<xs:schema id="CategoriesDS_AnnotatedName"
    targetNamespace=
    "http://www.tempuri.org/CategoriesDS_AnnotatedName.xsd"
    xmlns:mstns="http://www.tempuri.org/CategoriesDS_AnnotatedName.xsd"
    xmlns="http://www.tempuri.org/CategoriesDS_AnnotatedName.xsd"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"  xmlns:codegen="urn:schemas-microsoft-com:xml-msprop"  attributeFormDefault="qualified" elementFormDefault="qualified">
    <xs:element name="CategoriesDS_AnnotatedName" msdata:IsDataSet="true">
        <xs:complexType>
            <xs:choice maxOccurs="unbounded">
                <xs:element name="Categories"  codegen:typedName="Category"
                    codegen:typedPlural="Categorys">  <xs:complexType>
                        <xs:sequence>
                            <xs:element
                            name="CategoryID"
                            msdata:ReadOnly="true"
                            msdata:AutoIncrement="true"
                            type="xs:int" />
                            <xs:element
                            name="CategoryName"
                            type="xs:string"  codegen:typedName="Name" />  <xs:element
                            name="Description"
                            type="xs:string"
                            minOccurs="0" />
                        </xs:sequence>
                    </xs:complexType>
                </xs:element>
            </xs:choice>
        </xs:complexType>
        <xs:unique name="Constraint1" msdata:PrimaryKey="true">
            <xs:selector xpath=".//mstns:Categories" />
            <xs:field xpath="mstns:CategoryID" />
        </xs:unique>
    </xs:element>
</xs:schema> 

The sample code creates a strongly typed DataSet based on the Categories table in Northwind. The user specifies whether the one based on the default or annotated schema file is used. In either case, data is loaded into the DataSet and the collections of rows and columns in the DataSet are iterated over to display the data and to demonstrate the effect of the schema annotations.

The C# code is shown in Example 2-24.

Example 2-24. File: TypedDataSetNamesForm.cs
 // Namespaces, variables, and constants
using System;
using System.Configuration;
using System.Text;
using System.Data;
using System.Data.SqlClient;

// Table name constants
private const String CATEGORIES_TABLE = "Categories";

//  . . . 

StringBuilder result = new StringBuilder( );

// Create the DataAdapter.
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Categories",
    ConfigurationSettings.AppSettings["Sql_ConnectString"]);

if (annotatedRadioButton.Checked)
{
    // Create the typed DataSet with name annotations.
    CategoriesDS_AnnotatedName ds = new CategoriesDS_AnnotatedName( );
    // Fill the Categories table within DataSet.
    da.Fill(ds, CATEGORIES_TABLE);

    result.Append("Annotated Names" + Environment.NewLine +
        Environment.NewLine);
    // Iterate over the rows collection and display columns.
    // Note that the row collection is Categorys
    // and that each row is Category.
    foreach(CategoriesDS_AnnotatedName.Category row in ds.Categorys)
    {
        // Note that the CategoryName field is referred to simply as Name.
        result.Append(row.CategoryID + "\t" + row.Name + "\t" +
            row.Description + Environment.NewLine);
    }
}
else
{
    // Create the typed DataSet without name annotations.
    CategoriesDS ds = new CategoriesDS( );
    da.Fill(ds, CATEGORIES_TABLE);

    result.Append("Default" + Environment.NewLine + Environment.NewLine);
    // Iterate over the rows collection and display columns.
    foreach(CategoriesDS.CategoriesRow row in ds.Categories)
    {
        result.Append(row.CategoryID + "\t" + row.CategoryName + "\t" +
            row.Description + Environment.NewLine);
    }
}

resultTextBox.Text = result.ToString( ); 

Discussion

Annotations are modifications to the XSD schema used to generate a strongly typed DataSet that allows the names of elements in the strongly typed DataSet to be customized without changing the underlying schema. This allows more meaningful element names to be used resulting in code that is easier to read, use, and maintain. Table 2-16 lists available annotations.

Table 2-16. Available annotations

Annotation

Description

typedChildren

Name of the method that returns objects from a child data relation.

typedName

Name of the object.

typedParent

Name of the method that returns an object from a parent data relation.

typedPlural

Name of the collection of objects.

nullValue

Value or behavior if the underlying value is DBNull . Table 2-17 lists possible values for this annotation. The default value is _throw .

Table 2-17 describes possible values for the nullValue annotation.

Table 2-17. Values for nullValue annotation

nullValue

Description

Replacement Value

A value having the same type as the element to be returned

_empty

Return String.Empty for a String Return an object created from an empty constructor for other objectsThrow an exception for primitive types

_null

Return a null reference for objectsThrow an exception for primitive types

_throw

Raise an exception

Table 2-18 lists the different objects in a strongly typed DataSet and the default names and available annotations for each.

Table 2-18. Default values and available annotations for elements of strongly typed DataSet objects

Element

Default name

Annotation

DataTable

TableName DataTable

typedPlural

DataTable methods

New TableName RowAdd TableName RowDelete TableName Row

typedName

DataRowCollection

TableName

typedPlural

DataRow

TableName Row

typedName

DataColumn

DataTable . ColumnName ColumnDataRow . ColumnName

typedName

Property

PropertyName

typedName

Child accessor

Get ChildTableName Rows

typedChildren

Parent accessor

TableName Row

typedParent

DataSet events

TableName RowChangeEvent TableName RowChangeEventHandler

typedName

The use annotations, a reference to the codegen namespace, must be included in the XSD schema, as shown:

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

The codegen namespace allows the names of methods, properties, relations, constraints, and events in the strongly typed DataSet to be customized.