1.

DataSets

 

XML Schemas is one of the bedrocks of the XML world. Learning XML entails mastering of the XML schemas. The general notion is that after comprehending XML schemas, not only can you revolutionize the world, but can also generate substantial wealth for yourself, since thereafter, a hike in salary is a foregone conclusion.

 

Two programs named a.cs and b.cs need to be written, which would eventually launch us on a journey to XML Schemas.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet();

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

One of the primary applications of XML Schema is that of dealing with data in a database.

 

In the .Net world, data is stored in a DataTable object. While handling data, we seldom work with a single table and handle multiple tables simultaneously. The DataSet object bestowed upon the .Net world can deal with a group of DataTable objects, since a dataset could be comprised of one or more DataTable objects.

 

In the file a.cs, an object d of type DataSet is created by calling a constructor without any parameters. The DataSet class has a function called WriteXmlSchema, which creates the XML Schema file. In an effort to accomplish this, it takes a string parameter. This explains why we chose to furnish the name of the file b.xsd. By specifying the full path of ‘c:\b.xsd’, we have ensured that the file is created in the root of drive C. The file contains the following:-

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="NewDataSet" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded" />

</xs:complexType>

</xs:element>

</xs:schema>

 

Before we progress on to explaining the xsd file that gets written in the XML Schema Programming Language, we shall pen down yet another program named b.cs, which is aimed at displaying all the details of the xsd file.

 

Thus, we are performing two tasks simultaneously:  first, we are writing a DataSet object to disk as an XML schema; second, we are also reading the schema file to discover as to what this file format contains.

 

 

b.cs

using System;

using System.Data;

using System.Xml;

public class zzz

{

public static void Main()

{

XmlDataDocument d = new XmlDataDocument();

DataSet ds;

ds = d.DataSet;

ds.ReadXmlSchema("c:\\b.xsd");

DataTableCollection dtc = ds.Tables;

Console.WriteLine("Tables count=" + dtc.Count);

}

}

 

Output

Tables count=0

 

XmlDataDocument is a class that understands XML and data. Therefore, an object d, representing the class, is created in the file. The object contains a property called DataSet, which stands for the data and permits access to the xml data in the XmlDataDocument in the form of tables, views, rows, columns, relations, primary key, and so on.

 

The function ReadXmlSchema is used to read the xsd file. Hence, it is supplied along with the string parameter of c:\\b.xsd.

 

Every DataSet object is known to represent a large number of tables. Using the Tables property of class DataTableCollection, the variable dtc is initialized. The DataTableCollection class is derived from interface InternalDataCollectionBase and has a Count property, which provides a count of the number of tables in the collection. The resultant answer is displayed as zero.

 

This corroborates the fact that the file b.xsd is valid in the Schema world and that it is error-free.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet();

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

dtc.Add(t);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="NewDataSet" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

Output

c:\>xmlprg>b

Tables count=1

 

 

In a.cs, the modifications that had been effected, have resulted in the addition of a table to the DataSet object.

 

The Tables collection gives access to the DataTableCollection object, from which the Add function is called. This function is passed a freshly minted DataTable object. Whilst creating this object, the constructor is provided with a string parameter, which is the name of a table that is to be created, viz. 'vijay'.

           

When the program b.exe is executed, the Tables count displays a value of 1. Thus, it is indicative of a table definition lurking somewhere within the above xsd file.

 

Now, let us take a peek into the xsd file and unravel it in greater detail.

 

An xsd file is simply an xml file. All xml files begin with an xml declaration <?xml, followed by the version property, initialized to 1.0, and further followed by the standalone attribute property. The property is assigned a value of 'yes', thereby signifying that there is no external DTD. Every xsd file then has a tag <xs:schema.

 

Before we proceed further, we take a short diversion. In our earnest intent to expound the xsd file in an enhanced manner, we have resorted to simple files. Therefore, in file b.xsd located in the root directory, enter the following 2 lines and then, run the program b.exe on it.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema>

</xs:schema>

 

Error

Unhandled Exception: System.Xml.XmlException: 'xs' is an undeclared namespace. Line 2, position 2.

 

The exception gets generated since all the attributes pertaining to the tag xs:schema in the xsd file, have been eliminated. Click on the ‘No’ button when the debugging box appears.

The word xs, which has been placed before schema, is the namespace that the XML framework wants to familiarize itself with.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema xmlns:xs="mukhi">

</xs:schema>

 

No errors are generated on executing the file b.exe for the above xsd file, since the URI of the xs namespace has been set to 'mukhi' using the 'reserved' namespace of xmlns.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema1">

</xs:schema>

 

Error

Unhandled Exception: System.ArgumentException: The schema namespace is invalid. Please use this one instead: http://www.w3.org/2001/XMLSchema.

 

In the eventuality of specifying a wrong name, (as has occurred in the program) an exception that pinpoints this mistake, gets generated. Besides, a request is also made to use the valid namespace of http://www.w3.org/2001/XMLSchema.

 

When the above URL is entered in a browser, it connects to the website of www.w3.org and retrieves a page that describes the official specification of the XML schemas. Therefore, there is no necessity of supplying any other schema tags at all.

 

Let us now revert back and get a better grasp of each schema tag in the program generated xsd file.

<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

 

The schema tag embodies the definition of the schema. The first attribute 'id' is optional. The value displayed here is NewDataSet since no name has been assigned to the DataSet. On providing a value in the constructor of the DataSet class, the DataSet can be assigned a name that would then be displayed in lieu of NewDataSet. However, this attribute value must remain unique throughout the XML file.

 

The attribute targetNamespace has a value of the URI reference in the namespace. This namespace is utilized by all the schema components in the schema. The attribute xmlns also talks about the namespaces employed in the schema and hence, it is a URI reference. Since the xmlns:xs points to a certain URI, the rules governing all tags that start with 'xs', shall be stated in the URI document.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<aaa:schema xmlns:aaa="http://www.w3.org/2001/XMLSchema">

</aaa:schema>

 

The above xsd file displays no errors whatsoever, even though the namespace has been changed from xs to aaa. The name of the namespace is inconsequential, be it aaa or xs or xsd. It is merely by convention that the namespace xs is used universally, and we definitely wish to follow this convention.

 

The attribute msdata falls in the same category as xs and would be touched upon subsequently. There are innumerable other attributes that shall be dealt with progressively, since they are optional.

 

Now, let us venture onto the element tag. An element is the basic entity in an xsd file. The name is optional if the element is not specified within a schema tag. However, it is mandatory to have the name attribute if the element is within the schema and it must follow the namespace specifications.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="NewDataSet">

</xs:element>

</xs:schema>

 

The error depicted below shall be displayed if the name attribute is removed.

 

Error

Unhandled Exception: System.Xml.Schema.XmlSchemaException: The required attribute 'name' is missing. An error occurred at file:///c:/b.xsd(3, 2).

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="NewDataSet" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

</xs:schema>

<xs:element>

</xs:element>

 

The xsd file now shows no errors as the element tag is placed outside the schema boundaries.

 

The name attribute value may be modified to any other name  since the name that is assigned, does not have any bearing on the output at all. Thus, it need not always be NewDataSet.

 

The xsd world is highly case sensitive. Thus, if an attribute called 'name' is changed to 'Name', i.e. if the small letter 'n' is replaced by the capital letter 'N', the following exception gets thrown:

 

Error

Unhandled Exception: System.Xml.Schema.XmlSchemaException: The 'Name' attribute is not supported in this context. An error occurred at file:///c:/b.xsd(3, 16).

 

An element tag associates a name with a type. This element tag is then used to represent a table, a column and all other relational entities. An element can either represent a simple type or a complex one.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="NewDataSet" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

In this case, the IsDataSet attribute is set to 'true', thus indicating that whatever ensues is part of the DataSet world. If this attribute is eliminated, then instead of representing a DataSet, the element NewDataSet now refers to a table. The msdata embodies the rules pertaining to this attribute, since it is the namespace.

 

Everything that follows is part of a tag named complexType, which establishes the content type of the element. We have the option of assigning attributes to it, yet none of them have been utilized so far. This tag is a type definition for elements.

 

b.xsd

...

<xs:complexType name="aa">

<xs:element name="a1"/>

<xs:element name="a2"/>

<xs:element name="a3"/>

</xs:complexType>

...

<xs:element name="yyy" type="xs:aa">

...

yyy is defined as an element of type aa, where aa refers to a complex type defining a structure and information that pertains to the element. The tags placed in the complex types follow a specific rule.

 

The next tag that we encounter is the choice tag. A choice tag in the normal course is followed by a large number of element tags. The basic idea behind this tag is to select only one of the elements in the group for the element. It is used to choose one out of multiple choices.

 

The maxOccurs attribute with the choice tag determines the number of occurrences of the choice. A value of 10 represents 10 choices, while a value of 'unbounded' signifies that there is no upper ceiling at all on the number of choices.

 

In the xsd file, we have only one element. Therefore, presently, this tag is of no consequence. The element called 'vijay' evidently symbolizes the name of the table. Since there is no extra type information available for it, the complexType tag is left empty.

 

A shorter xsd file that specifies a single table, would be as follows:

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="yes" targetNamespace="http://www.tempuri.org/" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="yes" >

<xs:complexType>

</xs:complexType>

</xs:element>

</xs:schema>

 

The element tag must be followed by the complexType tag to enable the table 'yes' to be registered. The element name stands for the name of the table and not for its id.

 

Let us now proceed further.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

dtc.Add(t);

t = new DataTable("sonal");

dtc.Add(t);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

The above program introduces yet another instance of a DataTable object, which is created by passing the constructor a string value of 'sonal'. This results in the creation of a table called 'sonal'. Then, the Add function is brought into play to add this table to the collection of tables in the DataSet. Thus, in all, there are two tables called vijay and sonal in the DataSet object.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

</xs:complexType>

</xs:element>

<xs:element name="sonal">

<xs:complexType>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

Since the DataSet class has been assigned the name mukhi in a.cs, the schema for both the elements display an id of mukhi in the xsd file.

 

The element tag operates like a container. It now contains both the table definitions within a complexType definition. The choice tag also makes sense, since there are two elements in it, i.e. the two tables vijay and sonal. At this stage, no columns are added to the tables, therefore, the complexType within both the tables is empty.

 

While the xsd file is being read, a precise pattern has to be pursued. We start with the first element named mukhi. It specifies a DataSet mukhi, since the attribute IsDataSet has a value of 'true'. The complexType defines the dataset elaborately. The choice permits the display of only one element, or in this case, one table at a time. Further, the definitions for each table are placed within their own complexType tags, which at the moment are vacant.

 

b.cs

using System;

using System.Data;

using System.Xml;

public class zzz

{

public static void Main()

{

XmlDataDocument d = new XmlDataDocument();

DataSet ds;

ds = d.DataSet;

ds.ReadXmlSchema("c:\\b.xsd");

DataTableCollection dtc = ds.Tables;

Console.WriteLine("Tables count=" + dtc.Count);

for (int i = 0; i < dtc.Count; i++) {

DataTable t = ds.Tables[i];

Console.WriteLine("TableName=" + t.TableName);

DataColumnCollection dcc = t.Columns;

Console.WriteLine("Columns count=" + dcc.Count);

}

}

}

 

Output

Tables count=2

TableName=vijay

Columns count=0

TableName=sonal

Columns count=0

 

The b.cs program displays the names of the tables along with the count of the columns in each table.

 

The tables in the Table Collection are accessed using a 'for' loop statement. Every Data Table class has a member named TableName that possesses the table name. Thus, using this property, the table names are printed. Also, using the Columns property, which is of type DataColumnCollection, the column information can be obtained.

 

Every Collection has a property called Count, which provides the number of the objects that it contains. Thus, the Count property in the DataColumnCollection gives the number of columns that each table possesses. The result is displayed as zero for both the tables.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c = new DataColumn("Salary", ty );

tc.Add(c);

dtc.Add(t);

t = new DataTable("sonal");

ty = Type.GetType("System.String");

c = new DataColumn("Name", ty );

tc = t.Columns;

tc.Add(c);

dtc.Add(t);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

Output

Tables count=2

TableName=vijay

Columns count=2

TableName=sonal

Columns count=1

 

The above program adds columns to the tables named vijay and sonal. Two columns are added to the first table of vijay, while one column is added to the second table sonal.

 

In the program, the DataColumnCollection object that is associated with each table, is stored in an object tc. This has been achieved by employing the property Columns. We wish to re-iterate the fact that each DataTable has a DataColumnCollection object that stores the columns in that table. At this stage, no columns are defined, therefore, this object is null.

 

 

The Type class represents a type or a class. Using the static function GetType in this class, a string representation of a type is specified, so as to create an actual Type object. Thus, ty represents a String when System.String is supplied as a parameter. Note how the namespace has been explicitly used.

 

The DataColumn constructor accepts two parameters, viz. the name of the column i.e. FirstName, and the data type of the object i.e. Type. In order to add this DataColumn object to the DataColumnCollection object, the Add function in the object tc is called.

 

In the same manner, one more column named Salary with a data type of int or System.Int32, is added. After the addition of two columns to the table vijay, a column called Name, having a data type of String, is added to the second table sonal.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" minOccurs="0" />

<xs:element name="Salary" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="sonal">

<xs:complexType>

<xs:sequence>

<xs:element name="Name" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

In the xsd file, the two columns in the table vijay are visible along with some content in the complex type. Apart from this, a new tag called 'sequence' has been initiated here, which merely asks the elements to fall in line. Thus, the elements have to occur in a certain order or sequence. The element tag displays the name as FirstName, i.e. the name of the first column, along with an attribute of type, for the first time. The attribute value originates from the xs namespace.

 

A value of String in a C# data type gets converted into a string in an xsd file. This goes to prove that there is a mapping between the .Net data types and the schema data types.

 

The second column depicts a data type of int, since an Int32 structure maps to int in the schema world. The minOccurs attribute has a value of 0, which entails that it must occur for a minimum of zero times, i.e. this field is optional.

 

Henceforth, we shall not display the initial lines of the xsd file, since they shall persistently crop up in every file. Also, the code that gets iterated very often has not been displayed, in order to conserve the number of pages in the book.

 

b.cs

using System;

using System.Data;

using System.Xml;

public class zzz

{

public static void Main()

{

XmlDataDocument d = new XmlDataDocument();

DataSet ds;

ds = d.DataSet;

ds.ReadXmlSchema("c:\\b.xsd");

DataTableCollection dtc = ds.Tables;

Console.WriteLine("Tables count=" + dtc.Count);

for (int i = 0; i < dtc.Count; i++)

{

DataTable t = ds.Tables[i];

Console.WriteLine("TableName=" + t.TableName);

DataColumnCollection dcc = t.Columns;

Console.WriteLine("Columns count=" + dcc.Count);

for (int j = 0; j < t.Columns.Count; j++)

{

Console.WriteLine("ColumnName = " + t.Columns[j].ColumnName + " type = " + t.Columns[j].DataType);

}

}

}

}

 

Output

Tables count=2

TableName=vijay

Columns count=2

ColumnName = FirstName type = System.String

ColumnName = Salary type = System.Int32

TableName=sonal

Columns count=1

ColumnName = Name type = System.String

 

The above program has been embellished to delineate all the details of the columns in the table. The Count property supplies the count of the number of objects in the collection. Thus, we loop through the collection by using this property. Then, using the indexer of the DataTableCollection class, the DataTable object is retrieved and stored in object 't'.

 

The Columns Collection conducts itself in a manner similar to the Table Collection. Thus, t.Column[0] accesses Column 0 in the Column Collection. Thereafter, the Column Name is displayed using the ColumnName property, and the data type is displayed using the DataType property. In this manner, columns can be added, as well as, read from the xsd file.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

tc.Add(c1);

DataColumn [] a = new DataColumn[1];

a[0] = c;

t.PrimaryKey = a;

dtc.Add(t);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

The above program explains how a Primary Key can be added to the table. A primary key represents one or more columns in a table that contains unique values. It is mandatory to have a primary key in every table, so that one can uniquely identify each row in a table.

 

Apart from the two DataColumns, in order to add a primary key, an array of DataColumn object 'a' is created. This column, which now represents the Primary Key, is then assigned to the property called PrimaryKey in the Table object, so that it functions as the primary key. A point to be noted here is that the PrimaryKey property accepts an array of DataColumn objects and not a single object.

 

As is evident, the first member a[0] in the array of DataColumn object of size, is initialized to the DataColumn object c.

 

The xsd file that is created, has the Primary Key representation, following the XML Schema way of things.

 

b.xsd

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" />

<xs:element name="Salary" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

<xs:unique name="Constraint1" msdata:PrimaryKey="true">

<xs:selector xpath=".//vijay" />

<xs:field xpath="FirstName" />

</xs:unique>

</xs:element>

 

Everything appears analogous to what we had encountered earlier in the above xsd file. The only variation here is that there exists one more element tag of the DataSet following the initial complex type tag, which stores the details of the Primary Key.

 

The element commences with a tag of 'unique' to represent the Primary Key with the name of Constraint1. Notice that this tag has not been included in the DataTable 'vijay'. This is important, because even though the Primary Key is a part of the DataTable definition, it is stored as a component of the DataSet, and not of the Table.

 

 

The name does not necessarily represent the name of the primary key. It is generated by the system in such a manner that it remains unique across all the tags. The PrimaryKey attribute from the msdata namespace is set to 'true', hence all tags that follow it are deemed to be a Primary Key. 

 

The unique tag is followed by two more tags that describe the Primary Keys in greater detail.

 

The first such tag is 'selector'. The attribute xpath is an XPath that identifies the table name containing the Primary Key. The XPath expression has been explained in an earlier chapter. The field tag passes on the information about the column name that constitutes the Primary Key.

 

If the Primary Key is constituted of more than one column, i.e. if the array of DataColumn objects has a size of 2, and if it is assigned 2 columns as its members, then the xsd file would read as follows:

 

b.xsd

<xs:selector xpath=".//vijay" />

<xs:field xpath="FirstName" />

<xs:field xpath="Salary" />

 

Thus, contingent upon the number of columns constituting the primary key, an equal number of field tags are introduced. The same effect is achieved when the Unique property of the DataColumn is set, in lieu of the DataTable.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

tc.Add(c1);

dtc.Add(t);

DataRow r = t.NewRow();

r["FirstName"] = "Sonal";

r["Salary"] = 200;

t.Rows.Add(r);

Console.WriteLine("Row Count " + t.Rows.Count);

foreach( DataRow rr in t.Rows)

Console.WriteLine(rr[0] + " " + rr[1]);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

Output

Row Count 1

Sonal 200

 

Using the NewRow function present in the DataTable class, a new row can be added to the table. This function returns an object of datatype DataRow. However, this DataRow is an empty object. Therefore, the two fields have to be filled up manually.

 

This can be achieved by using the indexer with a string, i.e. the field name as the parameter. The other alternatives would be to either use a syntax such as r[0], where the field number is stated, or r[c] where c stands for the DataColumn object. As is very evident, there are a million ways to skin a cat !!!

 

This is not all. This row now has to be added to the RowsCollection that is associated with the table. The Rows property returns a DataRowCollection object, which in turn contains an Add function. This function takes a parameter of DataRow that is to be added.

Once the row has been added, the collection is checked for sanity purposes. Therefore as before, a 'for each' statement is used to verify the collection object. In the loop, rr gets initialized to a fresh DataRow from the Rows collection, and then, the indexer is used to display the two fields.

 

The output establishes the fact that the row has been added. However, surprisingly, on viewing the xsd file, there is apparently no mention of the data. This can be attributed to the fact that the WriteXmlSchema function writes only the schema and not the data.

 

Thus, in order to obtain a description of the DataSet in the xsd file, we merely need to replace the last function named WriteXmlSchema with the following line:

 

d.WriteXml("c:\\b.xsd");

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<mukhi>

<vijay>

<FirstName>Sonal</FirstName>

<Salary>200</Salary>

</vijay>

</mukhi>

 

The data is now visible in the file. You may ignore the file extension for the time being.

 

The file b.xsd now contains the name of the DataSet mukhi as the root tag. It is followed by the name of the table vijay, and thereon, the data is placed within the respective field names. Thus, it is advisable to write both the Write functions, one for writing the data WriteXml and the other for writing the schema WriteXmlSchema. But do ensure that they are assigned distinctive filenames.

 

 

 

Column Attributes

 

a.cs

....

c1 = new DataColumn("Salary", ty );

c1.DefaultValue = 20;

tc.Add(c1);

....

 

In the above program, the property DefaultValue is initialized to a value of 20. This property, as the name suggests, assigns a value to a field if it is not in possession of one. Thus, when no value is assigned to the field salary, it is guaranteed of being assigned a value of 20.

 

b.xsd

....

<xs:element name="FirstName" type="xs:string" minOccurs="0"/>

<xs:element name="Salary" type="xs:int" default="20" minOccurs="0" />

....

 

In the schema definition file, the element tag shows an attribute of 'default', which takes a value of 20. Thus, the property default in the DataColumn merely gets transformed into an attribute in the xsd file.

 

Now, replace the property of DefaultValue with the following line:

 

c1.Caption = "age";

The Caption property is generally utilized by controls, such as the DataGrid for providing a more aesthetically appealing display. If this property is not set, the ColumnName property is used instead.

 

The property representation in the xsd file is as follows

<xs:element name="Salary" msdata:Caption="age" type="xs:int" minOccurs="0" /> 

 

The attribute belongs to the namespace of msdata.

 

c1.AutoIncrement = true;

The AutoIncrement property is to be used when we want the system to increment the value of a column. Thus, when the property is turned 'on', the value cannot be modified manually. Instead, the system achieves it automatically.

 

<xs:element name="Salary" msdata:AutoIncrement="true" type="xs:int" minOccurs="0" />

The AutoIncrement property also becomes an attribute named AutoIncrement in the msdata space. The default for the AutoIncrement property is 'false'. However, if the property is initialized to 'false', it does not show up in the xsd file, since attributes with default values are not written.

 

c1.ReadOnly = true;

The ReadOnly attribute prohibits any changes to the data in the column. Obviously, we are allowed to add a row on the first occasion, but thereafter, changes are not permitted.

 

<xs:element name="Salary" msdata:ReadOnly="true" type="xs:int" minOccurs="0" />

 Most properties are converted into their equivalent attributes from the msdata namespace. The ReadOnly property is no exception to this rule. Therefore, in the xsd file, the attribute ReadOnly is initialized to 'true'.

 

c1.ExtendedProperties.Add("sonal","wife");

The ExtendedProperties property is of type PropertyCollection. It is used to store custom information with every column. The framework has no inclination towards determining its content. For e.g., it could be a number that gets flagged when the data stored in this column needs to be changed. Thus, it is entirely the user's prerogative to decide on what data to store, and how to dispense with the stored data.

 

Every collection object has an Add function. In the case of the Property collection, this function accepts two parameters, viz. the name of the property 'sonal' and its value 'wife'. The xsd file that gets created, displays the following additional line:

 

<xs:element name="Salary" msprop:sonal="wife" type="xs:int" minOccurs="0" />

These properties are associated with the column and not with the table or dataset. Therefore, they are reflected as attributes of the element or the column, salary.

 

The world of XML is ensconced in tags. An xml file can originate from any one of the numerous sources from all over the world. Therefore, the concept of a namespace is very central to the XML world.

 

The tag ‘name’ is very common. It could stand for the name of a friend, a supplier, a customer or even for man's best friend- a dog. It would be extremely difficult to infer what a tag name signifies, since everyone is at liberty to assign a different meaning to it. Thus, to overcome this lacuna, every tag has to be prefaced by a name or a prefix called the namespace.

 

Thus, the attribute of sonal is prefixed with the word msprop, which is the namespace. Further, the details about this namespace, the rules of the tag and the attribute usage, et al, would be stored at some place in the file. The custom attributes invariably take the form of name-value pairs in the msprop namespace.

 

c1.ExtendedProperties.Add("sonal","wife");

c1.ExtendedProperties.Add("vijay","husband");

Every entity called a Collection class provides the flexiblility of handling more than one of its kind. The above two lines effectively associate the two properties of 'sonal' and 'vijay' with the DataColumn Salary, which is clearly discernible in the xsd file.

 

<xs:element name="Salary" msprop:sonal="wife" msprop:vijay="husband" type="xs:int" minOccurs="0" />

The element of Salary is now displayed with two properties in the form of two separate name-value pairs. We can have innumerable attributes in an element tag.

 

c.MaxLength = 12;

The MaxLength property is more of an error-check. It is basically provided for apprizing the system that the length of the characters in the column should not exceed its value. The xsd file does not contain this data within itself for reasons unknown.  However, on changing the value of the DataColumn c, which is the string type column, FirstName and not the DataColumn c1 Salary of type int, the following exception gets generated:

 

Error

Unhandled Exception: System.ArgumentException: MaxLength applies to string data type only. You cannot set Column 'Salary' property MaxLength to be non-negative number.

 

Thus, the system does not permit this property to be added to a non-string type column, but neither does it make the property, a part of the xml schema.

 

c1.Namespace = "zzz";

Using the Namespace property, the Namespace of the DataColumn c1 is set to zzz.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema

xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:app1="zzz">

 <xs:import namespace="zzz" schemaLocation="b_app1.xsd" />

 <xs:element name="mukhi" msdata:IsDataSet="true">

 <xs:complexType>

 <xs:choice maxOccurs="unbounded">

 <xs:element name="vijay">

 <xs:complexType>

 <xs:sequence>

 <xs:element name="FirstName" type="xs:string" minOccurs="0"

 <xs:element ref="app1:Salary" minOccurs="0" />

 </xs:sequence>

 </xs:complexType>

 </xs:element>

 </xs:choice>

 </xs:complexType>

 </xs:element>

</xs:schema>

 

On accomplishing this, a new attribute or a prefix from the xmlns namespace called app1, gets created in the schema. This attribute is assigned the value of the namespace zzz. Thus, any attribute or tag prefaced with app1 belongs to the namespace zzz.

 

The 'import' tag identifies a namespace whose entities or schema components are referenced by this schema. The namespace attribute is the most vital one, since it specifies the name of the namespace, in our case, zzz. This is the URI reference to the namespace that is to be imported, since the members are being referenced from it.

 

In the xsd file, there exist attributes or tags that contain references to components in the namespace zzz. These references are through prefixes declared earlier in the schema tag. In our specific case, the prefix is called app1, which is declared with the xmlns attribute. If this attribute is omitted, then the current schema shall contain unqualified references.

 

The attribute schemaLocation has a value of b_app1.xsd that stands for a URI. It is a URI reference to the location of the schema document, which contains the imported namespace. If this attribute is absent, then the xml document figures out the identification of the schema on its own.

 

This file presently dwells on our disk in the current folder, where all our files are stored. Its contents are as follows:

 

b_app1.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="zzz">

<xs:element name="Salary" type="xs:int" />

</xs:schema>

 

c1.Prefix = "zzz";

The Prefix property of c1 is now initialized to zzz in the program.

 

<xs:element name="Salary" msdata:Prefix="zzz" type="xs:int" minOccurs="0" />

This sets the Prefix attribute in the msdata namespace to zzz. This is the XML prefix that aliases, or in other words, is another name for the namespace of the DataTable.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

tc.Add(c1);

c1 = new DataColumn("Chap", ty ,"Salary * 10", MappingType.Attribute);

tc.Add(c1);

dtc.Add(t);

DataRow r = t.NewRow();

r["FirstName"] = "Sonal";

r["Salary"] = 42;

t.Rows.Add(r);

r = t.NewRow();

r["FirstName"] = "tina";

r["Salary"] = 22;

t.Rows.Add(r);

d.WriteXmlSchema("c:\\b.xsd");

d.WriteXml("c:\\b.xml");

}

}

 

This program simply adds one more column to the already existing columns of Salary and FirstName. The different constructors in the DataColumn class take a varied number of parameters. The one used in the above program has four parameters.

 

The first parameter is the name of the column Chap. The second is the data type of the column Int32. The third column is the computed field. Therefore, it has been given a string that represents any valid expression. It multiplies the value in the field Salary by 10.

 

Any valid expression may be used here, while ensuring that the field names must be present in the table. Thus, having an expression of aa * 10 will throw the following exception

 

Error

Unhandled Exception: System.Data.EvaluateException: Cannot find column [aa].

 

The last parameter is an enum named MappingType that shall be explicated shortly. In addition to the column, two rows are added to the table. The XML file is also written to a separate file named b.xml.

 

b.xsd

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" minOccurs="0" msdata:Ordinal="0" />

<xs:element name="Salary" type="xs:int" minOccurs="0" msdata:Ordinal="1" />

</xs:sequence>

<xs:attribute name="Chap" msdata:ReadOnly="true" msdata:Expression="Salary * 10" type="xs:int" />

</xs:complexType>

</xs:element>

The xsd file includes only two elements or column names despite the presence of three columns. This is because, the third one being a computed column, does not qualify as a real column. This column is never assigned a value directly.

 

An extra attribute named Ordinal has been created. As always, it is a part of the msdata space, with a value of 0 for the first column, and 1 for the second column. This is by virtue of each column being assigned a number starting with zero and then, progressively increasing it by one.

 

The attribute tag is placed outside the sequence tag but inside the element tag vijay. The name is self-explanatory. An attribute is created, wherein the name attribute is the name of the computed column Chap. The ReadOnly property is set to 'true', since its value is incapable of being modified. The Expression attribute is an exact replica of the string parameter passed to the constructor.

 

b.xml

<?xml version="1.0" standalone="yes"?>

<mukhi>

<vijay Chap="420">

<FirstName>Sonal</FirstName>

<Salary>42</Salary>

</vijay>

<vijay Chap="220">

<FirstName>tina</FirstName>

<Salary>22</Salary>

</vijay>

</mukhi>

 

The xml file exhibits the two columns of FirstName and Salary as tags bound with their data values. The name of the table, vijay is associated with each row. Hence, it is seen twice, whereas the name of the DataSet mukhi is specified only once.

 

A point to be noted here is that the computed column is shown as an attribute of the table. As a result of this, the schema makes a clear demarcation. The computed column does not belong to the category of the table columns, as it is not assigned a value directly. It becomes an attribute to the table tag vijay.

 

Now, incorporate a small change to the above C# program by modifying the last parameter from MappingType.Attribute to MappingType.Element.

 

b.xsd

<xs:element name="Chap" msdata:ReadOnly="true" msdata:Expression="Salary * 10" type="xs:int" minOccurs="0" />

The change in the parameter modifies the column Chap from an attribute to an element, as is seen in the xsd file. Its ReadOnly property becomes 'true', whereas the Expression attribute remains unchanged. The minOccurs takes a value of zero, signifying that it is optional. The Ordinal attribute on both the earlier elements is not visible either.

 

b.xml

<?xml version="1.0" standalone="yes"?>

<mukhi>

<vijay>

<FirstName>Sonal</FirstName>

<Salary>42</Salary>

<Chap>420</Chap>

</vijay>

<vijay>

<FirstName>tina</FirstName>

<Salary>22</Salary>

<Chap>220</Chap>

</vijay>

</mukhi>

 

The xml file is more straightforward. Here, the value of 420 is enveloped within the field tag of Chap. It is no longer an attribute to the tag vijay.

 

Thus, MappingTypes deal only with the representation of the column data in the xml files, which may either be an attribute or an element.

 

There are two more options of the MappingType, which we will tackle now. Change the value of the last parameter to the DataColumn constructor from MappingType.Element to MappingType.Hidden.

 

<xs:attribute name="Chap" msdata:ReadOnly="true" msdata:Expression="Salary * 10" type="xs:int" use="prohibited" />

In the xsd file, the column shows up as an attribute as before, but this time, it has an extra attribute called 'use', which is set to 'prohibited'. This attribute determines the use of the attribute, and is permitted any one of five different values. The value of 'prohibited' obviously denies any access to the attribute.

 

In the xml file, there is no trace of the column Chap. It is because the value of 'hidden' assigned to the attribute, restricts the column from showing up in the xml file.

 

The last value that can be assigned to MappingType is MappingType.SimpleContent. However, this attribute can only be applied to a text element.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c = new DataColumn("Salary", ty );

c.AutoIncrement = true;

c.AutoIncrementSeed = 1000;

c.AutoIncrementStep = 10;

tc.Add(c);

dtc.Add(t);

DataRow r = t.NewRow();

t.Rows.Add(r);

r = t.NewRow();

t.Rows.Add(r);

d.WriteXmlSchema("c:\\b.xsd");

d.WriteXml("c:\\b.xml");

}

}

 

b.xsd

<xs:element name="Salary" msdata:AutoIncrement="true" msdata:AutoIncrementSeed="1000" msdata:AutoIncrementStep="10"

type="xs:int" minOccurs="0" />

 

A few lines in the .cs file have been modified. This results in the addition of the same column Salary as seen earlier, but this time, with a slight difference- the addition of three properties.

 

The first one is AutoIncrement, which is set to 'true', thereby allowing the system to change the property. The default value is obviously 'false'. The second property is AutoIncrementSeed, which decides on the starting value of the field salary, i.e. 1000. Finally, the Step attribute determines the incrementing factor each time. The default value of 1 is changed to 10.

 

The properties in the DataColumn class become attributes from the msdata namespace, as has been witnessed earlier on infinite occasions.

 

b.xml

<?xml version="1.0" standalone="yes"?>

<mukhi>

<vijay>

<Salary>1000</Salary>

</vijay>

<vijay>

<Salary>1010</Salary>

</vijay>

</mukhi>

 

The XML file renders a lucid insight into the functioning of the attributes.  Two rows are added in the program without any fields being initialized.

 

The FirstName tag or field is not seen in the XML output. Also, since the Salary field has the property of AutoIncrement set to 'true', the first row shows the initial value as 1000, while the second row displays a value of 1010, as a result of the increment factor being 10.

 

DataTable Properties

 

a.cs

DataTable t = new DataTable("vijay");

t.CaseSensitive = true;

DataColumnCollection tc = t.Columns;

 

b.xsd

<xs:element name="vijay" msdata:CaseSensitive="True">

 

Whenever string comparisons are made within the table, the case is taken into consideration. The string comparisons are normally performed at the time of sorting, searching and filtering the DataTable. Since this property is part of the DataTable class, it is shown as an attribute of CaseSensitive from the msdata namespace, in the element delineating a data table. The default value is 'false' and hence, it does not show up at all on most occasions.

 

t.MinimumCapacity = 100;

The MinimumCapacity property determines the minimum starting size of the table. At the time of creation of the table, if for instance, the system allocates 10 rows, and the data to be fetched is 1000 rows, then the size of the table would have to be expanded. This property is utilized for augmenting performance. With the above statement, the size is set to 100 rows.

<xs:element name="vijay" msdata:MinimumCapacity="100">

The property becomes a MinimumCapacity attribute in the msdata namespace.

 

t.Prefix = "zzz";

The Prefix property purports itself in a manner akin to what we have witnessed earlier in the case of a DataColumn, where it merely adds a simple attribute to the xsd file.

<xs:element name="vijay" msdata:Prefix="zzz">

 

DataSet Properties

 

We now venture into the realm of DataSet properties and use the DataSet object 'd' to set its properties. Introduce the following lines between the first and the last statement as shown below.

 

a.cs

...

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

DataColumn c1;

c1 = new DataColumn("Salary", ty );

tc.Add(c1);

d.Prefix="zzz";

dtc.Add(t);

DataRow r = t.NewRow();

r["FirstName"] = "Sonal";

r["Salary"] = 200;

t.Rows.Add(r);

...

 

b.xsd

...

<xs:element name="mukhi" msdata:IsDataSet="true" msdata:Prefix="zzz">

...

 

 

The Prefix property conducts itself in a fashion similar to the one described earlier for the DataColumn and DataTable. A simple attribute gets added to the element representing the data set.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

d.Namespace="zzz";

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

tc.Add(c);

tc.Add(c1);

dtc.Add(t);

DataRow r = t.NewRow();

r["FirstName"] = "Sonal";

r["Salary"] = 200;

t.Rows.Add(r);

Console.WriteLine("Row Count " + t.Rows.Count);

foreach( DataRow rr in t.Rows)

Console.WriteLine(rr[0] + " " + rr[1]);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

 

Output

Row Count 1

Sonal 200

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" minOccurs="0" />

<xs:element name="Salary" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

On setting the namespace property of the DataSet, a large number of attributes that are set for the schema element, come into play.

 

The targetNamespace is set to the namespace of zzz, which is a uri reference. Zzz is the namespace of all schema components in this schema file. The xmlns attribute too is set to zzz, which is a uri reference. Thus, all the components that are unqualified, belong to the zzz namespace.

 

Alternatively, if the attribute of xmlns:aaa ="yyy" is entered, then the ones with a prefix aaa belong to the yyy namespace. These namespace names are normally analogous to the xsd prefix http://www.w3.org/2001/XMLSchema, which is an entire URL. Here, we have employed a shorter name instead.

The attribute named attributeFormDefault can assume any of the two values of either 'qualified' or 'unqualified'. The default value is 'unqualified'. Its value reflects directly on the attributes that are present in the target namespace. It determines whether the attributes must be qualified or not. If the value is 'unqualified', then there is no requirement to qualify the attributes present, from the target namespace with the namespace prefix.

 

However, if the 'qualified' option is selected, as has been done by us, then the attributes from the target namespace have to be qualified with the namespace prefix. This setting is globally valid for each of the attributes, but it can easily be altered for each individual attribute, by using the 'form' attribute.

 

The elementFormDefault attribute is exclusive to elements, and it behaves in a manner similar to the earlier attribute that had dealt with the attributes. Now, replace the line of d.Namespace with the following:

 

a.cs

t.Namespace = "zzz";

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:app1="zzz">

<xs:import namespace="zzz" schemaLocation="b_app1.xsd" />

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element ref="app1:vijay" />

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

Akin to assigning a value to the namespace property of the DataSet, the namespace property of the table too can be set. The same restrictions that had existed thereon, apply here also, such as- in the creation of the prefix app1, etc.

 

The lone point to be borne in mind is that, no columns are visible whatsoever, even though there is a mention of the table vijay. Moreover, the 'ref' keyword is introduced with vijay. However, the DataSet of 'mukhi' still meets the eye.

 

The following lines have been inserted in the C# program:

 

a.cs

d.Namespace = "zzz";

t.Namespace = "zzz";

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified">

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" minOccurs="0" />

<xs:element name="Salary" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

The namespace of the DataSet mukhi and that of the table vijay are set to zzz. On doing so, the columns appear in the file and the 'ref' attribute is replaced with 'name' keyword.  Since the dataset and the datatable belong to the same namespace, no prefixes are added to any of the elements.

For the next program, let the two DataColumn objects belong to the zzz namespace.

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz {

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

d.Namespace="zzz";

t.Namespace="zzz1";

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

c.Namespace = "zzz";

c1.Namespace = "zzz";

tc.Add(c);

tc.Add(c1);

dtc.Add(t);

DataRow r = t.NewRow();

r["FirstName"] = "Sonal";

r["Salary"] = 200;

t.Rows.Add(r);

Console.WriteLine("Row Count " + t.Rows.Count);

foreach( DataRow rr in t.Rows)

Console.WriteLine(rr[0] + " " + rr[1]);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="zzz1">

<xs:import namespace="zzz1" schemaLocation="b_app1.xsd" />

<xs:element name="FirstName" type="xs:string" />

<xs:element name="Salary" type="xs:int" />

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element ref="app1:vijay" />

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

The element tag again has a 'ref' attribute added to it, but without any type element. This happens because 'type' and 'ref' are mutually exclusive. The 'ref' attribute merely indicates that this element would reference an element called FirstName, which dwells in the app1 namespace prefix. In turn, app1 points to the zzz1 namespace. The 'ref' attribute comes to the fore when the dataset namespace and the table namespace do not match.

 

a.cs

c.Namespace = "zzz1";

c1.Namespace = "zzz2";

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="zzz2" xmlns:app2="zzz1">

<xs:import namespace="zzz2" schemaLocation="b_app1.xsd" />

<xs:import namespace="zzz1" schemaLocation="b_app2.xsd" />

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element ref="app2:vijay" />

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

If we change the namespace of the first column to zzz1 and of the second column to zzz2 in the xsd file, then besides the namespace prefix for the table, two additional namespace prefixes viz. app1 and app3 are created. Thus, there are a total of three import tags. Now, reintroduce the following statement in a.cs:

 

d.Namespace="zzz";

t.Namespace="zzz";

...

...

c.Namespace = "zzz1";

c1.Namespace = "zzz2";

 

The xsd file now reveals that the first 'ref' attribute has FirstName prefixed with app1, and the second 'ref' attribute has salary prefixed with app2, since they belong to different namespaces.

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" targetNamespace="zzz" xmlns:mstns="zzz" xmlns="zzz" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" attributeFormDefault="qualified" elementFormDefault="qualified" xmlns:app1="zzz1" xmlns:app2="zzz2">

<xs:import namespace="zzz2" schemaLocation="b_app2.xsd" />

<xs:import namespace="zzz1" schemaLocation="b_app1.xsd" />

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element ref="app1:FirstName" minOccurs="0" />

<xs:element ref="app2:Salary" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

 

The salient point to be commited to memory is that, if the DataTable belongs to the zzz namespace and the DataSet belongs to the zzz namespace, then there would be no import statement for the table in the schema. The DataColumn may not belong to the same namespace. If it does, the element uses the name attribute. If it does not, the 'ref' attribute is used instead, but sans the type attribute.

 

You may experiment with various permutations and combinations to validate the above statement. For e.g.

c.Namespace = "zzz";

c1.Namespace = "zzz2";

 

There are some things in life that never change. So also is the case with the property of ExtendedProperties, which works in the same manner here as it did with the DataColumn class. The only modification being the addition of the property to the DataSet element mukhi.

 

d.ExtendedProperties.Add("cat","good");

 

<xs:element name="mukhi" msdata:IsDataSet="true" msprop:cat="good">

 

In one of the previous examples, we demonstrated two ways by which a column can possess unique values. Yet another way of achieving this is by creating an object of instance UniqueConstraint and passing the column c or FirstName, which must be unique, to its constructor.

As usual, the Constraints property is a collection object and hence, the Add function is required in order to add the constraint to it.

 

a.cs

UniqueConstraint u;

u = new UniqueConstraint(c);

t.Constraints.Add(u);

dtc.Add(t);

 

b.xsd

</xs:complexType>

<xs:unique name="Constraint1">

<xs:selector xpath=".//vijay" />

<xs:field xpath="FirstName" />

</xs:unique>

</xs:element>

 

In the xsd file, the same line gets written, as was the case with the Primary key or Unique property.

 

d.EnforceConstraints = false;

 

Setting the property EnforceConstraints to 'false' would result in a ban on all enforcements of the constraint. Thus, the xsd file remains almost identical, since the above property merely becomes an attribute in the msdata namespace with the same name.

 

<xs:element name="mukhi" msdata:IsDataSet="true" msdata:EnforceConstraints="False">

 

Foreign Key

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("mukhi");

DataTableCollection dtc;

dtc = d.Tables;

DataTable t = new DataTable("vijay");

DataColumnCollection tc = t.Columns;

DataColumn c,c1;

Type ty;

ty = Type.GetType("System.String");

c = new DataColumn("FirstName", ty );

tc.Add(c);

ty = Type.GetType("System.Int32");

c1 = new DataColumn("Salary", ty );

tc.Add(c1);

dtc.Add(t);

DataTable t1 = new DataTable("vijay1");

DataColumnCollection tc1 = t1.Columns;

DataColumn c2;

ty = Type.GetType("System.String");

c2 = new DataColumn("LastName", ty );

tc1.Add(c2);

dtc.Add(t1);

ForeignKeyConstraint f = new ForeignKeyConstraint(c2,c);

t.Constraints.Add(f);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="mukhi" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="mukhi" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="vijay">

<xs:complexType>

<xs:sequence>

<xs:element name="FirstName" type="xs:string" minOccurs="0" />

<xs:element name="Salary" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="vijay1">

<xs:complexType>

<xs:sequence>

<xs:element name="LastName" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

<xs:unique name="vijay1_Constraint1" msdata:ConstraintName="Constraint1">

<xs:selector xpath=".//vijay1" />

<xs:field xpath="LastName" />

</xs:unique>

<xs:keyref name="Constraint1" refer="vijay1_Constraint1" msdata:ConstraintOnly="true">

<xs:selector xpath=".//vijay" />

<xs:field xpath="FirstName" />

</xs:keyref>

</xs:element>

</xs:schema>

 

The above example expounds the concept of a Foreign Key Constraint.

 

Let us take a simple example to understand the concept of a Foreign Key. Every business has customers who are normally attended to, during the course of the daily business. In such a case, every customer is assigned a unique ID, and the details are placed in a table called customers. In most cases, this customer id column is made the Primary Key.

 

All the details of the sales made to these customers are documented in a Data Table such as 'sales'. Therefore, it contains the column called ID or CustomerID, which holds the id of the customer. However, in the sales table, the column is not unique across records, but the data in this column will originate from the same domain or from the same list of customers.

 

Thus, in the first table, the customer id is called the Primary Key, and wherever else this column appears, it is known as the Foreign Key. There is no means by which a value can be entered for the Foreign Key, unless such a value is already present as a Primary Key.

 

In XML, this Constraint is enforced by the class named ForeignKeyConstraint. This class also controls certain other issues. For example, it dictates as to what should happen to the foreign key value present in the other tables, incase the Primary Key gets deleted. For instance, should it be mandatory for the user to delete the Foreign Key values first? Should the System delete them? Or, should they remain as they are?

 

As always, the program starts by creating a table named vijay and furnishing it with two columns: the first column is named FirstName, which is represented by DataColumn object c, and the second column is called Salary. Next, it creates another table named vijay1 containing the columns of LastName and the DataColumn object c2.

 

The class ForeignKeyConstraint has a constructor that takes two DataColumn type parameters: the first is the parent column or the Primary Key, and the second is the child column. Thus, the LastName column is the Primary Key and the FirstName column is the Foreign Key.

 

The xsd file displays two tables: vijay with the columns FirstName and Salary, and vijay1 with the field LastName. Thereafter, at the end of the tables, but within the DataSet definition, two constraints are visible.

 

The tag is of type unique and is assigned the name vijay1_Constraint1 with the attribute ConstraintName from the msdata space as Constraint1. The unique tag asserts that the attribute or element must have a unique value within the scope. Thus, the name vijay1_Constraint1 must be unique within the schema.

 

The path points to the table vijay1. The field within that table is LastName since it is the Parent Key of the ForiegnKeyConstraint. Thus, it must be Unique or the Primary Key. Then, there is a tag called keyref. The name of the 'name' attribute remains the same as that of the unique tag property ConstraintName Constraint1. The 'refer' attribute has the same value as that of the 'name' attribute of the unique tag vijay1_Constraint1.

 

Thus, the above implies that the table 'vijay' mentioned in the selector tag xpath attribute and the field FirstName, must refer to the field LastName in the vijay1 table. As this relationship is a Constraint, the ConstraintOnly property is set to 'true'. This definitely is an allusive way of implementing a ForeignKeyConstraint !

 

a.cs

using System;

using System.IO;

using System.Xml;

using System.Data;

public class zzz

{

public static void Main()

{

DataSet d = new DataSet("vijay");

DataTable tC = new DataTable("Cust");

DataTable tO = new DataTable("Ord");

DataColumn c1 = new DataColumn("CID");

DataColumn c2 = new DataColumn("CName");

tC.Columns.Add(c1);

tC.Columns.Add(c2);

DataColumn c = new DataColumn("CusID");

tO.Columns.Add(c);

d.Tables.Add(tC);

d.Tables.Add(tO);

DataRelation dr = new DataRelation("custToOrders", c1 , c);

d.Relations.Add(dr);

d.WriteXmlSchema("c:\\b.xsd");

}

}

 

b.xsd

<?xml version="1.0" standalone="yes"?>

<xs:schema id="vijay" targetNamespace="" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

<xs:element name="vijay" msdata:IsDataSet="true">

<xs:complexType>

<xs:choice maxOccurs="unbounded">

<xs:element name="Cust">

<xs:complexType>

<xs:sequence>

<xs:element name="CID" type="xs:string" minOccurs="0" />

<xs:element name="CName" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="Ord">

<xs:complexType>

<xs:sequence>

<xs:element name="CusID" type="xs:string" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

<xs:unique name="Constraint1">

<xs:selector xpath=".//Cust" />

<xs:field xpath="CID" />

</xs:unique>

<xs:keyref name="custToOrders" refer="Constraint1">

<xs:selector xpath=".//Ord" />

<xs:field xpath="CusID" />

</xs:keyref>

</xs:element>

</xs:schema>

 

One of the more useful concepts in data base handling is that of a parent-child relationship, or a master-detail relationship, or a one-to-many relationship. The parent could be a list of customers, while the child could be the list of orders placed by each customer. We begin by creating a DataSet called vijay, and two tables named Cust and Ord.

 

The Cust table has two columns, CID for the Customer ID, and Cname for the name of the customer. The second table named ord only contains the Customer id in a column called CusID. Thus, for every unique customer id CID in the Cust table, there exist multiple records in the Ord table.

This relationship is expressed with the help of the class DataRelation.

 

In the constructor, the following are specified:

The name of the relation, custToOrders.

The 'parent' or the 'one' column c1, which is the column CID from the Cust table.

The 'child' or the 'many' column c from the Ord table.

 

This relation is then added to the dataset by using the Relations property and the Add function of the Collections object.

 

The xsd file as before, creates the two tables, viz. the Cust table with the columns CID and Cname, and the Ord table with the single column CusID. Then, we encounter the unique tag, which has the name property set to Constraint1, which points to the table Cust and field name CID, by using the selector tag. To refresh your memory, this field is the Primary Key.

 

The keyref now uses the name that is supplied to the DataRelation constructor, custToOrders. In the earlier case, the name was generated by the system. The 'refer' attribute points to the constraint Constraint1. It is this attribute that determines the field in the table that is to be referred to and not the name property.

 

The keyref obviously points to the Ord table and to the field CusID, since this is child key. The variation from the Foreign Key example is that, there is no attribute called ConstraintOnly, since this is not a constraint. It is merely a way of describing as to how the data that is spread over multiple tables, is to be related with each other.