4

DataSets

 

The focus in this chapter once again veers on to web services, but in the context of DataSets. A DataSet is a collection of DataTable objects, which in turn, embody data.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Data;

public class zzz

{

[WebMethod]

public DataSet abc()

{

DataTable t = new DataTable("vijay");

DataColumn c;

c = new DataColumn();

c.DataType = System.Type.GetType("System.Int32");

c.ColumnName = "c1";

t.Columns.Add(c);

c = new DataColumn();

c.DataType = System.Type.GetType("System.String");

c.ColumnName = "c2";

t.Columns.Add(c);

DataSet d = new DataSet();

d.Tables.Add(t);

return d;

}

}

 

The opening line in the abc function within the asmx file, creates a DataTable object called t. The parameter supplied to the constructor of the DataTable object is the name of the table, 'vijay'.

 

Every DataTable object is composed of columns, wherein, every single column is represented by a DataColumn object. After creating an instance c of the DataColumn class, the DataType property in the DataColumn class is initialized to the data type of the column, which in this case, is an int. Therefore, the static GetType function from the Type class is called with the full name of the type as its parameter.

 

The ColumnName property assigns a name to the column, which is c1 in this case. The DataTable has a Columns Collection property called Columns, which stores all the DataColumn objects. With the help of the Add function that is incorporated in it, the column named c1 is added to the Columns collection.

 

As a consequence, the Columns property now holds one column. A fresh instance of the DataColumn object is created with the DataType of string, and is named as c2. This DataColumn object too is added to the columns collection. Thus, the collection now is in possession of two columns, viz. c1 and c2. Finally, a DataSet d comprises of the Tables collection property Tables and employs the services of the Add function within it, to add the table t to the dataset.

 

A Polite Reminder

 

The steps to be taken in the order of succession, are as follows:

     Enter the url as http://localhost/a.asmx?WSDL in the browser.

     Copy the contents displayed on the browser into the file aa.wsdl under the www subdirectory.

     Ensure that the first line begins at the very first column.

 

     Change the url from http://localhost/a.asmx to http://localhost:8080/asmx.

     Finally, start the Trace utility program from the SOAP toolkit program option, to view the SOAP request and the SOAP response packets.

     Run the batch file, z.bat, stipulated in the previous chapter, in order to obtain the output, as depicted in the chapter.

 

Now, let us closely scrutinize a DataSet representation in a wsdl file. We have displayed only the relevant portions of the file, which are adequate for corroborating our explanation.

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:import namespace="http://www.w3.org/2001/XMLSchema" />

  <s:element name="abc">

<s:complexType />

  </s:element>

  <s:element name="abcResponse">

<s:complexType>

  <s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="abcResult" nillable="true">

  <s:complexType>

<s:sequence>

  <s:element ref="s:schema" />

  <s:any />

</s:sequence>

  </s:complexType>

</s:element>

  </s:sequence>

</s:complexType>

  </s:element>

  <s:element name="DataSet" nillable="true">

<s:complexType>

  <s:sequence>

<s:element ref="s:schema" />

<s:any />

 

  </s:sequence>

</s:complexType>

  </s:element>

</s:schema>

 

The abc element is devoid of all content, as the function does not accept any parameters. Next, we stumble upon and encounter a new element called 'import', which introduces all the schema definitions in the XmlSchema document, thereby, facilitating references to them in the schema document.

 

In the element abcResponse, the element of abcResult has a complexType. The complexType for the abcResult element contains two elements:

     The first element is the sequence, followed by another element, which currently has no name or type. Instead, it has a ref attribute. By using the ref attribute, other elements that were created earlier, are now accessible. In this case, a reference is made to the 'schema' element.

     The second element in the complex type is the element  'any', which permits access to all elements from any namespace.

 

There is yet another element called 'DataSet', which has the 'schema' element and the 'any' element, just like the earlier element. A salient point that needs to be emphasized here is that, in the wsdl representation of a DataSet object, there is neither any mention of a table named 'vijay', nor is there any sign of the columns or other details, such as the Primary Key, etc. The client program merely calls the abc function, without any parameters.

 

a.cs

public class aaa

{

public static void Main()

{

zzz a = new zzz();

a.abc();

}

}

 

The proxy code in zzz.cs dispatches back the Dataset that has been returned from the abc function. The DataSet is saved in the first member of the 'results' array.

 

zzz.cs

public System.Data.DataSet abc()

{

object[] results = this.Invoke("abc", new object[0]);

return ((System.Data.DataSet)(results[0]));

}

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

<abcResult>

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

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

<xsd:complexType>

<xsd:choice maxOccurs="unbounded">

<xsd:element name="vijay">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="c1" type="xsd:int" minOccurs="0" />

<xsd:element name="c2" type="xsd:string" minOccurs="0" />

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:choice>

</xsd:complexType>

</xsd:element>

 </xsd:schema>

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1" />

</abcResult>

</abcResponse>

 

A DataSet, as is represented in a SOAP payload, embodies all the relevant information.

 

 

The abcResult element commences with a child element called schema from the XMLSchema namespace, as is indicated by the namespace prefix xsd. The msdata namespace prefix is initialized to a uri. The value currently assigned to it is the one furnished by Microsoft.

 

The schema element is normally assigned an id that represents the name of the DataSet. However, since we have not supplied an id, the default name of NewDataSet has been used. The targetNamespace and the default namespace are both set to null.

 

Within the schema, there exists an element with the same name of NewDataSet, but this refers to the name of the DataSet, since the IsDataSet attribute is set to true.

 

The complexType that defines the structure of the DataSet, contains a 'choice' element that allows the selection of only one of the many elements that are present. In this case, since there is only a single element named 'vijay', the choice element is of no utility at all. For the uninitiated reader, we reiterate that 'vijay' is the name of a DataTable, as specified in the asmx file.

 

The DataTable element, i.e. 'vijay' is capable of having multiple occurrences. It has a sequence with two column names, c1 and c2. Assigning the value of zero to minOccurs is indicative of the fact that it is not obligatory to specify their values.

 

The last element is called a diffgram. A diffgram is an XML serialization format that carries both, the original as well as, the current data. This is pressed into service for marshalling a dataset over. We shall delve deeper into this concept as we make a headway in the book. Here, we have merely declared two namespaces, as there is no data present in our table.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Data;

public class zzz

{

[WebMethod]

public DataSet abc()

{

DataTable t = new DataTable("vijay");

DataColumn c;

c = new DataColumn();

c.DataType = System.Type.GetType("System.Int32");

c.ColumnName = "c1";

t.Columns.Add(c);

c = new DataColumn();

c.DataType = System.Type.GetType("System.String");

c.ColumnName = "c2";

t.Columns.Add(c);

DataRow r = t.NewRow();

r["c1"] = 1;

r["c2"] = "hi";

t.Rows.Add(r);

r = t.NewRow();

r["c1"] = 100;

r["c2"] = "Bye";

t.Rows.Add(r);

DataSet d = new DataSet("sonal");

d.Tables.Add(t);

return d;

}

}

 

Here, we have not displayed the files named aa.wsdl, a.cs and zzz.cs since the code contained in them remains unaltered.

 

Soap response

<abcResult>

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

<xsd:element name="sonal" msdata:IsDataSet="true">

</xsd:element>

</xsd:schema>

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

<sonal xmlns="">

<vijay diffgr:id="vijay1" msdata:rowOrder="0" diffgr:hasChanges="inserted">

  <c1>1</c1>

  <c2>hi</c2>

</vijay>

<vijay diffgr:id="vijay2" msdata:rowOrder="1" diffgr:hasChanges="inserted">

  <c1>100</c1>

  <c2>Bye</c2>

</vijay>

</sonal>

</diffgr:diffgram>

</abcResult>

</abcResponse>

 

After creating a DataRow object r, the NewRow function of the DataTable is put into operation in order to introduce a fresh row. The indexers, with the names of the columns c1 and c2, have been initialized to selected values, in order to fill up data in the two columns. Finally, by employing the Rows collection, this newly initialized row is added to the DataTable.

 

This process is iterated to add another row to the DataTable 'vijay', thereby, resulting in two rows in the table. The name of the Dataset is also changed to 'sonal'. However, no variation is witnessed in either the wsdl file or the proxy or the server because, the only innovation effected was, the introduction of data into the DataTable. 

 

In the SOAP response, the id of the schema and element reflect the new value of 'sonal'. We have not displayed the remaining portion of the schema file, since it remains unchanged.

 

The wsdl file demonstrates that the abcResult element commences with a schema element, which may then be followed by any other element, from any namespace. Here, the SOAP response contains the diffgram element, which creates the namespace prefixes msdata and diffgr. A diffgram may contain data from multiple namespaces. The next element is the dataset name 'sonal'.

 

A Dataset, in turn, is constituted of multiple tables. In addition to the above, there exists another dataset called 'vijay'. Hence, an element called 'vijay' is created. This element is assigned an id of vijay1, and the second record is allocated an id of vijay2. The rowOrder attribute is akin to a row number, where the first record is numbered as 0, the second as 1, and so on.

 

The hasChanges attribute is assigned the value of 'inserted'. This is followed by the data of the row, enclosed within tags, comprising of the names of the columns. Accordingly, a DataSet first dispatches the schema, followed by the actual data.

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Data;

public class zzz

{

[WebMethod]

public DataSet abc()

{

DataTable t = new DataTable("vijay");

DataColumn c;

c = new DataColumn();

c.DataType = System.Type.GetType("System.Int32");

c.ColumnName = "c1";

t.Columns.Add(c);

c = new DataColumn();

c.DataType = System.Type.GetType("System.String");

c.ColumnName = "c2";

t.Columns.Add(c);

DataRow r = t.NewRow();

r["c1"] = 1;

r["c2"] = "hi";

t.Rows.Add(r);

r = t.NewRow();

r["c1"] = 100;

r["c2"] = "Bye";

t.Rows.Add(r);

DataTable t1 = new DataTable("mukhi");

c = new DataColumn();

c.DataType = System.Type.GetType("System.Int32");

c.ColumnName = "d1";

t1.Columns.Add(c);

c = new DataColumn();

c.DataType = System.Type.GetType("System.String");

c.ColumnName = "d2";

t1.Columns.Add(c);

r = t1.NewRow();

r["d1"] = 1000;

r["d2"] = "hi23";

t1.Rows.Add(r);

r = t1.NewRow();

r["d1"] = 2000;

r["d2"] = "Bye23";

t1.Rows.Add(r);

DataSet d = new DataSet("sonal");

d.Tables.Add(t);

d.Tables.Add(t1);

return d;

}

}

 

Soap response

<abcResult>

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

<xsd:element name="sonal" msdata:IsDataSet="true">

<xsd:complexType>

<xsd:choice maxOccurs="unbounded">

<xsd:element name="vijay">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="c1" type="xsd:int" minOccurs="0" />

<xsd:element name="c2" type="xsd:string" minOccurs="0" />

</xsd:sequence>

</xsd:complexType>

</xsd:element>

<xsd:element name="mukhi">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="d1" type="xsd:int" minOccurs="0" />

<xsd:element name="d2" type="xsd:string" minOccurs="0" />

</xsd:sequence>

</xsd:complexType>

</xsd:element>

</xsd:choice>

</xsd:complexType>

 </xsd:element>

</xsd:schema>

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

<sonal xmlns="">

<vijay diffgr:id="vijay1" msdata:rowOrder="0" diffgr:hasChanges="inserted">

  <c1>1</c1>

  <c2>hi</c2>

  </vijay>

<vijay diffgr:id="vijay2" msdata:rowOrder="1" diffgr:hasChanges="inserted">

  <c1>100</c1>

  <c2>Bye</c2>

  </vijay>

<mukhi diffgr:id="mukhi1" msdata:rowOrder="0" diffgr:hasChanges="inserted">

  <d1>1000</d1>

  <d2>hi23</d2>

  </mukhi>

<mukhi diffgr:id="mukhi2" msdata:rowOrder="1" diffgr:hasChanges="inserted">

  <d1>2000</d1>

  <d2>Bye23</d2>

  </mukhi>

  </sonal>

</diffgr:diffgram>

</abcResult>

 

As before, aa.wsdl, a.cs and zzz.cs remain unchanged. Therefore, they are not displayed again. The asmx file, which now incorporates a voluminous code, has an additional DataTable object named t1, which refers to the table named 'mukhi'.

 

Two columns named d1 and d2 are added to the table. They are of type int and string, respectively. Thereafter, two records are added to this table and eventually, the table is added to the table collections of the DataSet.

The wsdl file merely contains information about a DataSet. It does not embody any information about the schema or the data in the tables. The client and the proxy remain unaltered for the same reasons as specified in the earlier examples.

 

As is customary, the large SOAP packet that is sent across, commences with the schema element. The id and the name of the first child element are initialized to 'sonal', where 'sonal' represents a DataSet, since the IsDataSet attribute is set to true.

 

Then the complexType describes the DataSet object 'sonal'. It enjoys the discretion of selecting from amongst the two elements of 'vijay' and 'mukhi'. This is because the DataSet object contains two tables with these names. The choice element permits the selection of only one element from amongst available set of elements.

 

Each of the tables is followed by its data structure, comprising of the columns, along with their data types, represented as child elements. The minOccurs, with a value of zero, can be omitted in the SOAP payload, if so desired. The same rules also apply to the second table called 'Mukhi'.

 

As per convention, the diffgram starts with the name of the dataset i.e. 'sonal', with the default namespace set to null. Two child elements, beginning with 'vijay' are visible, since the table 'vijay' consists of two records or rows. The same holds good for the two records in the table 'mukhi', thus, resulting in the display of two child elements with the name of 'mukhi'.

 

To begin with, in the case of both the tables, the 'rowOrder' has the value 0, and the 'hasChanges' is assigned the value of 'inserted', for each of the four records. Consequently, it is the rows of the table 'vijay' that are displayed first, followed by the rows of the table 'mukhi'.

 

The rest of the diffgram, i.e. the content embodied in the column names, remains unchanged. The id field assigns a unique name to each and every record in the diffgram.

 

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System.Web.Services;

using System.Data;

using System.Data.SqlClient;

public class zzz

{

[WebMethod]

public DataSet abc()

{

DataSet d ;

SqlConnection c = new SqlConnection("server=(local)\\NetSDK;database=pubs;uid=sa;pwd=;");

SqlDataAdapter da = new SqlDataAdapter ("select * from Authors where au_lname='White'",c);

d = new DataSet("vijay");

da.Fill(d, "ppp");

return d;

}

}

 

SOAP response

<abcResult>

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

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

<xsd:complexType>

<xsd:choice maxOccurs="unbounded">

<xsd:element name="ppp">

<xsd:complexType>

<xsd:sequence>

<xsd:element name="au_id" type="xsd:string" minOccurs="0" />

<xsd:element name="au_lname" type="xsd:string" minOccurs="0" />

<xsd:element name="au_fname" type="xsd:string" minOccurs="0" />

<xsd:element name="phone" type="xsd:string" minOccurs="0" />

<xsd:element name="address" type="xsd:string" minOccurs="0" />

<xsd:element name="city" type="xsd:string" minOccurs="0" />

<xsd:element name="state" type="xsd:string" minOccurs="0" />

<xsd:element name="zip" type="xsd:string" minOccurs="0" />

<xsd:element name="contract" type="xsd:boolean" minOccurs="0" />

 </xsd:sequence>

  </xsd:complexType>

  </xsd:element>

  </xsd:choice>

  </xsd:complexType>

  </xsd:element>

  </xsd:schema>

<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">

<vijay xmlns="">

<ppp diffgr:id="ppp1" msdata:rowOrder="0">

<au_id>172-32-1176</au_id>

<au_lname>White</au_lname>

<au_fname>Johnson</au_fname>

<phone>408 496-7223</phone>

<address>10932 Bigge Rd.</address>

<city>Menlo Park</city>

<state>CA</state>

<zip>94025</zip>

<contract>true</contract>

</ppp>

</vijay>

</diffgr:diffgram>

</abcResult>

 

So far, the DataTable has been created manually, which is not a very pragmatic way of working. An enhanced and workable mechanism would be, to have the DataSet created automatically, while records are being fetched from a database. The DataSet class and the classes related to it, originate from the 'System.Data' namespace, while the actual Data handling classes emanate from the 'System.Data.SqlCient' namespace.

 

Firstly, d is created as an object that appears analogous to the DataSet class. Then, an SqlConnection object is created with the connection string as a parameter to the constructor. The string passed to the constructor, determines the table in the database on SQL Server, with which a connection is to be established. A database is a mere collection of tables. The tables that hold our interest are contained in a database called pubs. Finally, the userid and password are specified. These individual pieces of data are separated by semi-colons and contain the words 'server', 'uid' and 'pwd', which are reserved by SQL Server. Thus, we are compelled to employ these words.

 

The Authors table lies in the 'pubs' database and is installed by the .NET samples. While the installation of the samples is in progress, the InstMSDE program installs a server on every machine. In our case, the server that has been installed is 'localhost'.

 

Next, an SqlDataAdapter object is created where the constructor accepts an SQL select statement and the newly created SqlConnection object. Further, the DataSet object is instantiated with the name 'vijay', provided through the constructor. The Fill function in the SqlDataAdapter initiates the retrieval of data from the database.

 

Firstly, a connection is actually made to the table in the database. Then, the select statement retrieves all the records from the Authors table, where the field au_lname in the record has a value of 'White'. If the 'where' clause is not been specified, it would result in the extirpation of all the records from the table.

 

The Fill function fills up the DataSet object d, which is passed as the first parameter. The data retrieved from the Authors table is named as ppp, which is the second parameter. Finally, the DataSet contains a single table, which is despatched across the wire.

 

The SOAP response is not disconcerted about the source of the data, i.e. whether the data originates from a database or it has been entered manually. As always, it displays the name of the dataSet 'vijay', followed by the second parameter of the Fill function, i.e. ppp. This is followed by the innumerable names of the fields from the Authors table, comprising of the entire data schema. Details, such as the relationships amongst tables, the Primary Key etc., are not disclosed. The diffgram simply specifies the DataSet name 'vijay', and a single element ppp, with all the data posited within the columns. Now, we progress and press on to a simple example using a DataGrid.

a.asmx

<%@ WebService Language="C#" class="ccc" %>

using System.Web.Services;

using System.Data;

using System.Data.SqlClient;

public class ccc : WebService {

[WebMethod]

public DataSet abc()

{

DataSet d  = new DataSet();

SqlConnection co = new SqlConnection("server=(local)\\NetSDK;database=northwind;Trusted_Connection=yes");

SqlDataAdapter c = new SqlDataAdapter("Select * from Customers", co);

c.Fill(d, "pqr");

return d;

}

}

 

zzz.cs

using System;

using System.Drawing;

using System.Windows.Forms;

using System.Data;

public class zzz : Form {

DataGrid d;

public zzz()

{

ClientSize = new Size(600, 413);

ccc c = new ccc();

DataSet ds = c.abc();

d = new DataGrid();

d.Size = new Size(584, 336);

d.Location = new Point(8, 8);

d.DataSource = ds;

d.DataMember = "pqr";

Controls.Add(d);

}

public static void Main()  {

Application.Run(new zzz());

}

}

s.bat

del *.exe

del *.dll

wsdl.exe /l:CS aa.WSDL

csc.exe /t:library ccc.cs

csc.exe zzz.cs /r:ccc.dll

zzz

 

Screen 4.1

 

Within this asmx file, there exists a function abc, which merely returns a DataSet that is loaded with the records from the Customers table. Thereafter, to create a wsdl file from the above asmx file, the URL of http://localhost/a.asmx?WSDL is specified in the browser. The file contents are saved in aa.wsdl.

 

Once this has been accomplished, the batch file s.bat is run, in order to execute a series of commands. The proxy code in the file ccc.cs contains a class ccc, which is referred to extensively in the client program zzz.cs.

 

For those of you who have not read our book on WinForms, it is important to know that the client program zzz.cs is a Windows applications, which displays a DataGrid control in a window. This DataGrid object d has a property called DataSource, which is initialized to a DataSet object that contains the data displayed by the data grid. The DataMember property, when specified, refers to the name of the table in the data set, whose data is to be displayed. In this case, the name of the table is 'pqr'.

 

Thus, it is amply evident that the program is strikingly similar to the earlier programs; the only dissimilarity being that the DataSet feeds a DataGrid object. The code in the asmx file that actually fetches or creates the DataSet, remains unaltered when employed by any application. This is due to the fact that the DataSet ds gets initialized by the code in the abc function.

 

If you are desirous of acquiring enhanced conceptual clarity, the most optimum technique would be to modify the port number of localhost to 8080, and then, view the packets in the output window of the Trace program. The SOAP response packet displays the data in a similar fashion, as was witnessed earlier.

 

Master-Detail Relationships: 

 

a.asmx

<%@ WebService Language="C#" class="ccc" %>

using System.Web.Services;

using System.Data;

using System.Data.SqlClient;

public class ccc : WebService

{

[WebMethod]

public DataSet abc()

{

DataSet c = new DataSet();

SqlConnection co = new SqlConnection("server=(local)\\NetSDK;database=northwind;Trusted_Connection=yes");

SqlDataAdapter a1 = new SqlDataAdapter("Select * from Customers", co);

SqlDataAdapter a2 = new SqlDataAdapter("Select * from Orders", co);

a1.Fill(c, "Customers");

a2.Fill(c, "Orders");

return c;

}

}

 

aa.xsd

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

  <xsd:element name="Customers">

    <xsd:complexType>

      <xsd:all>

        <xsd:element name="CustomerID" type="xsd:string"/>

      </xsd:all>

    </xsd:complexType>

  </xsd:element>

  <xsd:element name="Orders">

    <xsd:complexType>

      <xsd:all>

        <xsd:element name="OrderID" msdata:ReadOnly="True" msdata:AutoIncrement="True" type="xsd:int"/>

        <xsd:element name="CustomerID" minOccurs="0" type="xsd:string"/>

      </xsd:all>

    </xsd:complexType>

  </xsd:element>

  <xsd:element name="ddd" msdata:IsDataSet="true">

    <xsd:complexType>

      <xsd:choice maxOccurs="unbounded">

        <xsd:element ref="Customers"/>

        <xsd:element ref="Orders"/>

      </xsd:choice>

    </xsd:complexType>

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

      <xsd:selector xpath=".//Customers"/>

      <xsd:field xpath="CustomerID"/>

    </xsd:unique>

    <xsd:keyref name="ppp" refer="Constraint1">

      <xsd:selector xpath=".//Orders"/>

      <xsd:field xpath="CustomerID"/>

    </xsd:keyref>

  </xsd:element>

</xsd:schema>

 

zzz.cs

using System;

using System.Windows.Forms;

using System.Drawing;

using System.Data;

public class zzz : Form

{

ddd d;

DataGrid d2;

DataGrid d1;

public zzz()

{

d2 = new DataGrid();

d = new ddd();

d1 = new DataGrid();

d2.Location = new Point(8, 256);

d2.Text = "d2";

d2.Size = new Size(584, 248);

d2.ForeColor = System.Drawing.SystemColors.WindowText;

d2.BackColor = System.Drawing.SystemColors.Window;

d2.DataSource = d;

d2.DataMember = "Customers.ppp";

d1.Location = new System.Drawing.Point(8, 8);

d1.Text = "d1";

d1.Size = new Size(584, 240);

d1.ForeColor = System.Drawing.SystemColors.WindowText;

d1.BackColor = System.Drawing.SystemColors.Window;

d1.DataSource = d;

d1.DataMember = "Customers";

Text = "Customer Orders";

ClientSize = new Size(600, 581);

Controls.AddRange(new Control[] {d2,d1});

ccc c = new ccc();

DataSet ds = c.abc();

d.Merge(ds);

}

public static void Main(string[] args)

{

Application.Run(new zzz());

}

}

 

a.bat

del *.exe

del *.dll

del aa.cs

xsd /d aa.xsd

wsdl.exe /l:CS aa.WSDL

csc /t:library ccc.cs

csc /t:library aa.cs

csc zzz.cs /r:ccc.dll /r:aa.dll

zzz

 

Screen 4.2

 

This program displays data in datagrids, which functions on the foundation of a master-detail relationship. The application has two data-grids:

     One displays data from the Customers table.

     The other displays data from the Orders table with regard to a specific customer.

The Orders table is comprised of a very large number of records, as all orders have been stored in a single file. While the above program is being employed, and when we click on an individual customer in the first data grid, the orders only of that particular customer get displayed in the second grid. To facilitate this, a parent-child or master-detail or one-to-many relationship has to be established between the Customers table and the Orders table.

 

The zzz.cs file has two DataGrid objects, d1 and d2. The program is larger in size than the earlier ones, since properties like ForeColor and BackColor have been assigned specific values. The DataSource member for both the DataGrid objects is initialized to d, which is an instance of class ddd, whereas, the DataMember for the DataGrid d1 is Customers, and that for the DataGrid d2 is Customers.ppp.

 

The AddRange function is then used to add the two DataGrid objects d1 and d2 into an array. Finally, an object c is created, which is an instance of ccc. The class ccc is the WebService class from the file a.asmx. The abc function in this class is responsible for retrieving the data from the Customers and Orders table.

 

Within the abc function in the asmx file, two tables viz. Customers and Orders, are filled up with data by using the Fill function, and the dataset is then returned. Thus, the DataSet ds contains two tables. This DataSet ds is merged with the ddd object d.

 

The object of class ddd is used primarily for the data schema or rules. The DataSet object ds supplies the raw data. The relationship between the Orders table and the Customers table is stored in the ddd class. Therefore, it is mandatory on our part to figure out who creates the ddd class, and how it gets created.

 

In order to accomplish this, we commence with the file aa.xsd. We shall not probe any deeper into the details of the XML Schema world, except to state that it is one of the three most salient pillars of the XML world. Schemas are basically used for documentation. In this particular case, it is used for a relationship between tables. It is employed to ensure that the XML file conforms to the rules that are imposed on it.

 

The root tag is schema, with the xsd namespace pointing to a well-known URI. The targetNamespace and default namespace are null, while the other namespace prefix defined is msdata. The id attribute has no utility at this juncture.

 

The next tag or element in sequence is that of Customers. This name has been obtained from the name of our Table.  As every element needs a type, the Customers element is assigned the type 'complexType'. Within it, is ensconced a list of elements that constitute the definition of Customer. In our case, there is only one item named CustomerID, of type string. Normally, all the fields from the Customer table are listed out.

 

The same procedure is adopted for the Orders table too. Here, the two columns listed out are OrderID and CustomerID, consisting of the attributes ReadOnly and AutoIncrement, which are self-explanatory. The most crucial element is the one named ddd. Since the IsDataSet attribute has the value of true, therefore, using the xsd program on the aa.xsd file, it creates a class named ddd, which is derived from the class DataSet containing tons of code.

 

The complexType tag is followed by the choice element, which enables the user to select from amongst multiple elements that follow. There are two elements to choose from, and both of these refer to the Customers and Order elements, using the ref attribute. The keyref called ppp, refers to a constraint called Constraint1, which is created by using a unique element.

 

The XPath expression of //Customers in the selector element indicates a 'node from anywhere'. This is so because the symbol // stands for 'any location'. An XPath expression is generally used when some data has to be located in a node. In our code, the field of interest is the CustomerID. Close on the heels of the selector, is one more XPath expression having the value of //Orders, bearing in mind that the Xpath field is now specified as CustomerID.

 

Thus, we have been triumphant in building a relationship, where every CustomerID record in the Customer table is mapped to multiple CustomerID records in the Orders table. As an outcome of this, the DataMember of the second grid takes on the notation of Customers.ppp. It is so because ppp now is the keyref, which displays only those records that have the same Customer id as the active customer in the first grid.

 

A snippet of code from the aa.cs file that is created by the xsd program, has been displayed below.

 

aa.cs

CustomersDataTable tableCustomers;

OrdersDataTable tableOrders;

DataRelation relationppp;

private void InitClass()

{

DataSetName = "ddd";

Namespace = "";

tableCustomers = new CustomersDataTable();

Tables.Add(tableCustomers);

tableOrders = new OrdersDataTable();

Tables.Add(this.tableOrders);

tableOrders.Constraints.Add(new ForeignKeyConstraint("ppp", new DataColumn[] { tableCustomers.CustomerIDColumn}, new DataColumn[] {

tableOrders.CustomerIDColumn}));

relationppp = new DataRelation("ppp", new DataColumn[] {

tableCustomers.CustomerIDColumn}, new DataColumn[] {

tableOrders.CustomerIDColumn}, false);

Relations.Add(this.relationppp);

}

 

The InitClass function gets called by the constructor of the ddd class, wherein, the DataSetName property is set to ddd and the Namespace is set to null.

 

Thereafter, two tables named tableCustomers and tableOrders are created as instances of the classes derived from DataTable. Both the tables are added to the Tables collection. The most crucial statement here is the Foreign Key constraint called ppp. A foreign key of any table is always a Primary key of some other table. In our case, CustomerID is a primary key in the Customers table, whereas, it dons the mantle of a Foreign key in any other table that embodies it.

The constructor of the Foreign Key Constraint is assigned two DataColumn objects:

     The CustomerID from the Customers table as the Primary key.

     The CustomerID column from the Orders table as the Foreign key.

 

Once a DataRelation object has been created, it then becomes the bounden duty of the system to maintain this relation or Constraint.  This constraint will ensure that no record is added in the Order table, unless it contains a CustomerID that is present in the Customers table. The DataRelation retrieves records in a similar manner. If we bypass the xsd file, we would have to enter the above code manually, which is obviously a very tedious task.