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.