In this chapter, you will learn how to:
Create a DataSet programmatically
Use a DataSet in XML Web services
Apply filters to a DataSet
The .NET Framework class library includes the System.Data.DataSet class to represent relational data in memory. A DataSet is typically the result of a SQL database query. If you have used ADO.NET to access a database, you will have encountered the DataSet. However, you might not know that the DataSet has a number of uses outside the world of databases and that XML Web service projects can benefit from the features that the DataSet class provides. In this chapter, we introduce the DataSet class and its relationship with XML Web services. To demonstrate the use of the DataSet class, we will build on the example from Chapter 10, to create a more useful and sophisticated tool for viewing the history of credit card validation requests.
A full discussion of the DataSet class is beyond the scope of this book. In this section, we provide background information about the DataSet that is necessary to understand the remainder of this chapter. You should consult the .NET Framework SDK documentation for more detailed information.
For our purposes, you can think of the DataSet class as containing one or more tables, just like those in a relational database. Each table is composed of columns and rows. The table’s columns define what kind of data each element in a row can contain. A table is populated by assigning data values to each row in the table. The following diagram uses our credit card validation data to illustrate the relationship between rows, columns, tables, and a DataSet. We also identify the .NET Framework classes that are used to create each element.
Although the DataSet is most commonly used to store data from a relational database, it can be used to represent any tabular data. DataSet objects are disconnected, meaning that no connection is open between the database and the data contained in the DataSet. The DataSet acts as a simplified relational database and can be queried and updated independent of the original data.
Because DataSet objects are disconnected, changes made to a DataSet are not applied to the database automatically. A DataSet maintains details of any changes, but these changes must be explicitly applied to the original database as required. This approach is ideal for XML Web services, enabling you to pass the results of complex queries to clients for processing. Clients can then manipulate the DataSet and return it so that any changes can be applied to the database. Because a DataSet is disconnected from the database, the XML Web service client does not need to be able to communicate directly with the database application.
Later in this chapter, we will build an XML Web service that creates a DataSet programmatically (without a database). This approach not only simplifies the project that you will build (because you will not have to configure a SQL database) but also clearly demonstrates that a DataSet can be used without databases.
In the previous chapter, we added a feature to the credit card validation XML Web service to maintain a history of validation requests. The history of requests is available to clients through the GetValidationHistory method. In this chapter, we will modify this method to return the validation history as a DataSet and will develop a new Windows Forms client that allows the user to selectively view parts of the history.
We will add a new method, called CreateDataSet, that accepts an ArrayList of ValidationObject instances and creates a DataSet containing the history of validation requests. We will modify the GetValidationHistory method to retrieve the ArrayList from the Application state object and call the new CreateDataSet method. The DataSet that we create will contain a single table (named HistoryTable) composed of three columns: one for the type of card, one for the card number, and one for the result of the validation request. Each ValidationObject in the ArrayList will be represented as a row in the table. The following diagram illustrates the structure of the DataSet. We will use the name ValidationHistory for our DataSet object.
Procedure 11-1. Create the Project
Open the project that you created in Chapter 10. The URL for the project should be http://localhost/XMLWebServices/Chapter10/StateManagement/ValidatorService.
Select Copy Project from the Project menu, and copy the project to http://localhost/XMLWebServices/Chapter11/DataSets/ValidatorService.
Open the new project in Visual Studio .NET, and then save the solution file for the new project.
Procedure 11-2. Update the XML Web Service Class
In Solution Explorer, right-click on the Validation.asmx file and select View Code from the shortcut menu. This opens the Validation.asmx code-behind file (either Validation.asmx.cs or Validation.asmx.vb).
Add the following statements to the code file to define the CreateDataSet method.
The CreateDataSet method creates the DataSet illustrated earlier. The statements create the DataSet in the following manner:
Create a new DataSet with the name ValidationHistory.
Create a new DataTable with the name HistoryTable, and add it to the Tables property of the DataSet.
Create new DataColumn objects to represent the card type, the card number, and the result of the validation request, and then add each of these columns to the table created in the previous step.
Create a new DataRow object for each ValidationObject in the ArrayList and add it to the DataTable.
These steps show how simple it is to create and populate a DataSet programmatically, emphasizing the flexibility of this class. The benefits of representing the validation request history in a more structured form (as opposed to the multiline string we used in the previous chapter) will become clear when we create the XML Web service client later in the chapter. Notice that we have not applied the WebMethod attribute to the CreateDataSet method. This method will only be called from within the Web service class, and we do not expose this functionality to clients.
Example 11-1. C#
private DataSet CreateDataSet(ArrayList p_list) { // create the data set DataSet x_data_set = new DataSet("ValidationHistory"); // create the DataTable object DataTable x_table = new DataTable("HistoryTable"); // add the table to the data set x_data_set.Tables.Add(x_table); // create the columns, and add them to the table x_table.Columns.Add( new DataColumn("CardType", typeof(String))); x_table.Columns.Add( new DataColumn("CardNumber", typeof(String))); x_table.Columns.Add( new DataColumn("NumberValid", typeof(bool))); // run through the items in the ArrayList and add them // as rows to the data tables foreach (ValidationObject x_object in p_list) { // create a new DataRow to hold DataRow x_data_row = x_table.NewRow(); // set the data for the row from the // details of the ValidationObject x_data_row["CardType"] = x_object.o_card_type.ToString(); x_data_row["CardNumber"] = x_object.o_card_number; x_data_row["NumberValid"] = x_object.o_valid; // add the row to the table x_table.Rows.Add(x_data_row); } // return the DataSet return x_data_set; }
Example 11-2. Visual Basic .NET
Private Function CreateDataSet(ByVal p_list As ArrayList) _ As DataSet ‘ create the data set Dim x_data_set As DataSet = New DataSet("ValidationHistory") ‘ create the DataTable object Dim x_table As DataTable = New DataTable("HistoryTable") ‘ add the table to the data set x_data_set.Tables.Add(x_table) ‘ create the columns, and add them to the table x_table.Columns.Add(New DataColumn("CardType", _ Type.GetType("System.String"))) x_table.Columns.Add(New DataColumn("CardNumber", _ Type.GetType("System.String"))) x_table.Columns.Add(New DataColumn("NumberValid", _ Type.GetType("System.Boolean"))) ‘ run through the items in the ArrayList and add them ‘ as rows to the data tables Dim x_object As ValidationObject For Each x_object In p_list ‘ create a new DataRow to hold Dim x_data_row As DataRow = x_table.NewRow() ‘ set the data for the row from the ‘ details of the ValidationObject x_data_row("CardType") = x_object.o_card_type.ToString() x_data_row("CardNumber") = x_object.o_card_number x_data_row("NumberValid") = x_object.o_valid ‘ add the row to the table x_table.Rows.Add(x_data_row) Next ‘ return the DataSet Return x_data_set End Function
Replace the existing GetValidationHistory method with the following code.
Our revised GetValidationHistory method now obtains the ArrayList of ValidationObjects by using the application state support and then calls the CreateDataSet method to generate the DataSet to send to the XML Web service client.
Example 11-3. C#
[WebMethod] public DataSet GetValidationHistory() { // get the array list from the application state object ArrayList x_list = (ArrayList)Application["ValidationObjects"]; // check to ensure that we have a list to work with - if the // value returned from the Application object is null, this // is the first request made to the XML Web service method, // and we should create the list for use by future calls if (x_list == null) { x_list = new ArrayList(); Application["ValidationObjects"] = x_list; } // create the DataSet using the ArrayList of // ValidationObjects DataSet x_data = CreateDataSet(x_list); // return the history data return x_data; }
Example 11-4. Visual Basic .NET
<WebMethod()> _ Public Function GetValidationHistory() As DataSet ‘ get the array list from the application state object Dim x_list As ArrayList = _ CType(Application("ValidationObjects"), ArrayList) ‘ check to ensure that we have a list to work with - if the ‘ value returned from the Application object is null, this ‘ then is the first request made to the XML Web service method, ‘ and we should create the list for use by future calls If x_list Is Nothing Then x_list = New ArrayList() Application("ValidationObjects") = x_list End If ‘ create the DataSet using the ArrayList of ‘ ValidationObjects Dim x_data As DataSet = CreateDataSet(x_list) ‘ return the history data Return x_data End Function
Build the project by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.
The Windows Forms client that we create in this section is identical to the one we created in Chapter 10. In fact, we need this client only to generate requests that will form the validation history.
Procedure 11-3. Create the Windows Form
Copy the client template project to C:XMLWebServicesSBSProjectsChapter11DataSets. For detailed instructions, see the sidebar "XML Web Service Client Projects" in Chapter 7.
Open the new client project in the Chapter11DataSets folder in Visual Studio .NET.
Open Form1 in design view.
Change the Size property of Form1 to 288, 272.
Add the following controls to Form1, and then configure their properties as specified in the table:
Control | Property | Value |
---|---|---|
Label | Text | Total Requests Made: |
TextAlign | MiddleRight | |
Location | 40, 168 | |
Size | 120, 23 | |
Label | Text | Delete the contents of the Text property value. |
TextAlign | MiddleLeft | |
Name | TotalRequests | |
Location | 176, 168 | |
Size | 96, 23 | |
Label | Text | Total Invalid Card Numbers: |
TextAlign | MiddleRight | |
Location | 8, 200 | |
Size | 152, 23 | |
Label | Text | Delete the contents of the Text property value. |
TextAlign | MiddleLeft | |
Name | TotalInvalid | |
Location | 176, 200 | |
Size | 96, 23 |
If you have added the controls correctly, the form should look like this:
Procedure 11-4. Add the Web Reference
Add a Web reference to the ValidatorService XML Web service that we created earlier in the chapter. The URL for the ValidatorService service is http://localhost/XMLWebServices/Chapter11/DataSets/ValidatorService/Validation.asmx . (For details about adding a Web reference, see the section "Creating a Web Reference" in Chapter 4.)
In Solution Explorer, right-click the Web reference and select Rename from the shortcut menu. Rename the Web reference Validator.
Procedure 11-5. Edit the Code File
Double-click on the Validate button in the Form1 design view so that you can add the code that will be executed when the user clicks the button.
Add the following code to complete the ValidateButton_Click method.
The statements responsible for obtaining the statistics from the XML Web service are marked in bold. The ValidateButton_Click method now makes two calls to the XML Web service, one to request the card number validation (via the ValidateCard method) and another to get the ClientStats object (via the GetClientStatistics method).
Example 11-5. C#
private void ValidateButton_Click(object sender, System.EventArgs e) { // create the ValidationObject that we will send to // the XML Web service ValidationObject x_object = new ValidationObject(); // set the card type for the validation object based // on the user selection in the CardType ComboBox switch (CardType.Text) { case "AMEX": x_object.o_card_type = CARD_TYPE.AMEX; break; case "MasterCard": x_object.o_card_type = CARD_TYPE.MASTERCARD; break; case "VISA": x_object.o_card_type = CARD_TYPE.VISA; break; } // set the card number value in the ValidationObject x_object.o_card_number = CardNumber.Text; try { o_service.ValidateCard(ref x_object); // set the text of the result label based // on the response from the XML Web service if (x_object.o_valid) { Result.Text = "Number Valid"; } else { Result.Text = "Number Invalid"; } } catch (System.Web.Services.Protocols.SoapException x_ex) { switch (x_ex.Code.ToString()) { case "Client.IllegalCharacter": Result.Text = "Illegal Character"; break; case "Client.InvalidLength": Result.Text = "Invalid Length"; break; case "Client.InvalidPrefix": Result.Text = "Invalid Prefix"; break; default: Result.Text = "Unexpected Error"; break; } } // get the client statistics from the XML Web service ClientStats x_state = o_service.GetClientStatistics(); TotalRequests.Text = x_state.o_total_requests.ToString(); TotalInvalid.Text = x_state.o_failed_validations.ToString(); }
Example 11-6. Visual Basic .NET
Private Sub ValidateButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles ValidateButton.Click ‘ create the ValidationObject that we will send to ‘ the XML Web service Dim x_object As ValidationObject = New ValidationObject() ‘ set the card type for the validation object based ‘ on the user selection in the CardType ComboBox Select Case CardType.Text Case "AMEX" x_object.o_card_type = CARD_TYPE.AMEX Case "MasterCard" x_object.o_card_type = CARD_TYPE.MASTERCARD Case "VISA" x_object.o_card_type = CARD_TYPE.VISA End Select ‘ set the card number value in the ValidationObject x_object.o_card_number = CardNumber.Text Try o_service.ValidateCard(x_object) ‘ set the text of the result label based ‘ on the response from the XML Web service If x_object.o_valid Then Result.Text = "Number Valid" Else Result.Text = "Number Invalid" End If Catch x_ex As System.Web.Services.Protocols.SoapException Select Case x_ex.Code.ToString() Case "Client.IllegalCharacter" Result.Text = "Illegal Character" Case "Client.InvalidLength" Result.Text = "Invalid Length" Case "Client.InvalidPrefix" Result.Text = "Invalid Prefix" Case Else Result.Text = "Unexpected Error" End Select End Try ‘ get the client statistics from the XML Web service Dim x_state As ClientStats = o_service.GetClientStatistics() TotalRequests.Text = x_state.o_total_requests.ToString() TotalInvalid.Text = x_state.o_failed_validations.ToString() End Sub
Add the following statements to define the ValidatorService proxy class as an instance variable of Form1.
These statements should be inserted immediately after the declaration of the Form1 class. The following excerpt shows the Form1 class declaration. The statements that add the instance variable are marked in bold.
Add the following statements (marked in bold) to the constructor of the Form1 class. These statements initialize the ValidatorService class for use in the ValidateButton_Click method.
Visual Basic .NET programmers will have to expand the region labeled Windows Form Designer generated code in the text editor by clicking on the + sign.
Example 11-9. C#
public Form1() { // // Required for Windows Form Designer support // InitializeComponent(); // create the proxy object o_service = new ValidatorService(); // set the cookie container, so that the proxy object // will be able to correctly use cookie to provide // state information o_service.CookieContainer = new System.Net.CookieContainer(); // get the client statistics from the XML Web service ClientStats x_state = o_service.GetClientStatistics(); TotalRequests.Text = x_state.o_total_requests.ToString(); TotalInvalid.Text = x_state.o_failed_validations.ToString(); }
Example 11-10. Visual Basic .NET
Public Sub New() MyBase.New() ‘This call is required by the Windows Form Designer. InitializeComponent() ‘Add any initialization after the InitializeComponent() call ‘ create the proxy object o_service = New ValidatorService() ‘ set the cookie container, so that the proxy object ‘ will be able to correctly use cookie to provide ‘ state information o_service.CookieContainer = New System.Net.CookieContainer() ‘ get the client statistics from the XML Web service Dim x_state As ClientStats = o_service.GetClientStatistics() TotalRequests.Text = x_state.o_total_requests.ToString() TotalInvalid.Text = x_state.o_failed_validations.ToString() End Sub
Build the solution by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.
We will now create an XML Web service client that makes use of the DataSet class returned by the GetValidationHistory Web method. Earlier in the chapter, we briefly detailed some of the ways that a DataSet can be used within an XML Web service even without using a relational database. However, we did not explain why you might want to do this: some of the Windows Forms and Web Forms graphical user interface (GUI) components have special support for working with a DataSet. This support enables us to build more sophisticated (and useful) clients without creating more complex XML Web services. System.Data.DataView is one of the key classes that provide this support, and we will use it here to create an XML Web service client that allows the user to apply a simple filter to the list of validation requests obtained from the Web service. The filter displays only requests for a specific type of card.
Consult the Visual Studio .NET documentation for details about how other GUI components work with a DataSet.
Procedure 11-6. Create the Windows Form
Create a new Visual Studio .NET project using either the C# or Visual Basic .NET Windows Application project template. In the New Project dialog box, name the project ValidationHistoryClient and specify the location C:XMLWebServicesSBSProjects Chapter11DataSets.
Make the following changes to the properties of Form1.
Property | Value |
---|---|
Text | Validation History Client |
Size | 368, 320 |
Add the following controls to Form1, and then configure their properties as specified in the following table.
Control | Property | Value |
---|---|---|
DataGrid | CaptionText | Validation History |
ReadOnly | True | |
Name | HistoryDataGrid | |
RowHeadersVisible | False | |
Location | 10, 10 | |
PreferredColumnWidth | 110 | |
Size | 340, 176 | |
Label | Text | Filter: |
TextAlign | MiddleRight | |
Location | 42, 202 | |
ComboBox | Text | All |
Items | All AMEX VISA MasterCard | |
Name | FilterComboBox | |
Location | 178, 202 | |
Size | 96, 21 | |
Button | Text | Get History |
Name | HistoryButton | |
Location | 122, 250 | |
Size | 104, 32 |
If you have added the controls correctly, the form should look like the following screen shot.
Procedure 11-7. Add the Web Reference
Add a Web reference to the ValidatorService XML Web service that we created in the previous section. The URL for the ValidatorService service is http://localhost/XMLWebServices/Chapter11/DataSets/ValidatorService/Validation.asmx. (For detailed instructions about how to add a Web reference, see the section "Creating a Web Reference" in Chapter 4.)
Right-click the Web reference in Solution Explorer, and then select Rename from the shortcut menu. Rename the Web reference Validator.
Procedure 11-8. Edit the Code File
Double-click on the Get History button in the Form1 design view so that you can add the code that will be executed when the user clicks the button.
When the Get History button is clicked, the GetValidationHistory Web method is called and a DataSet is returned. We create a new DataView, which we will use to filter the data shown to the user. The DataView class works with individual tables from a DataSet, and we use the DataSet.Tables property to access the first (and only) table available. We then use the DataGrid.DataSource property to set the data that the DataGrid will display. Finally, we reset the ComboBox to display the default value of All.
Example 11-11. C#
private void HistoryButton_Click(object sender, System.EventArgs e) { // get the dataset from the XML Web service DataSet x_set = new ValidatorService().GetValidationHistory(); // create a dataview using the table in the dataset DataView x_view = new DataView(x_set.Tables[0]); // set the datasource for the grid to be the newly // created data view HistoryDataGrid.DataSource = x_view; // reset the combobox to All FilterComboBox.Text = "All"; }
Example 11-12. Visual Basic .NET
Private Sub HistoryButton_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles HistoryButton.Click ‘ get the dataset from the XML Web service Dim x_set As DataSet _ = New ValidatorService().GetValidationHistory() ‘ create a dataview using the table in the dataset Dim x_view As DataView = New DataView(x_set.Tables(0)) ‘ set the datasource for the grid to be the newly ‘ created data view HistoryDataGrid.DataSource = x_view ‘ reset the combobox to All FilterComboBox.Text = "All" End Sub
Scroll to the top of the Form1 code view, and then add the following statement to import the namespace of the proxy class.
Select the Form1.cs [Design] (C#) or Form1.vb [Design] (Visual Basic .NET) editor window, and double-click on the ComboBox so that you can add the code that will be executed when the user selects a value from the drop-down list. Add the following code so that the FilterComboBox_SelectedIndexChanged method is complete.
Example 11-15. C#
private void FilterComboBox_SelectedIndexChanged(object sender, System.EventArgs e) { // get the DataView from the DataGrid DataView x_view = (DataView)HistoryDataGrid.DataSource; // apply the filter if (FilterComboBox.Text == "All") { // use the default filter so that all of the data // is displayed to the user x_view.RowFilter = ""; } else { // the user has selected a specific card type to display, // so set the row filter accordingly x_view.RowFilter = "CardType=‘" + FilterComboBox.Text + "‘"; } }
Example 11-16. Visual Basic .NET
Private Sub FilterComboBox_SelectedIndexChanged(ByVal sender As _ System.Object, ByVal e As System.EventArgs) Handles _ FilterComboBox.SelectedIndexChanged ‘ get the DataView from the DataGrid Dim x_view As DataView _ = CType(HistoryDataGrid.DataSource, DataView) If (Not x_view Is Nothing) Then ‘ apply the filter If FilterComboBox.Text = "All" Then ‘ use the default filter so that all of the data ‘ is displayed to the user x_view.RowFilter = "" Else ‘ the user has selected a specific card type to ‘ display, so set the row filter accordingly x_view.RowFilter = "CardType=‘" + FilterComboBox.Text + "‘" End If End If End Sub
Build the solution by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.
Procedure 11-9. Create the Validation Requests
Start the client application.
Using Windows Explorer, execute the client application we created earlier in this chapter. C# programmers can find the file at C:XMLWebServicesSBSProjectsChapter11DataSetsWindowsFormsClientindebugWindowsFormsClient.exe. Visual Basic .NET programmers will find the file at C:XMLWebServicesSBS ProjectsChapter11DataSetsWindowsFormsClientinWindowsFormsClient.exe.
Using the information in the following table, enter a credit card type and number pair into the client, clicking the Validate button after each entry.
Card Type | Card Number |
---|---|
VISA | 4921 8352 2155 2042 |
VISA | 1234 5678 1234 5677 |
AMEX | 123456 |
MasterCard | Baddata |
Procedure 11-10. Test the XML Web Service Client
Press Ctrl+F5 to start the Validation History Client application within Visual Studio .NET.
Click on the Get History button.
This client requests the DataSet from the XML Web service and uses the DataGrid to display the complete set of details that you entered above. The following screenshot shows the validation request display.
Select VISA from the set of combo box values.
The display now shows only the requests for VISA card types, as shown here:
Select All from the set of combo box values.
The display now shows all of the requests, including those for AMEX and MasterCard numbers.
We are able to filter the set of results displayed in the DataGrid by using the RowFilter property of the DataView class. RowFilter is a String that accepts simple SQL expressions. For example, when you want to view only VISA requests, we set the RowFilter property to
"CardType=‘VISA’"
This filter selects only those rows in the DataTable in which the value of the CardType column is "VISA". The DataView class ensures that the DataGrid displays only the rows that match the filter.
Although we have built a very simple client, we have provided the user with a more sophisticated approach to viewing the validation history. The DataView retains the rows that do not match the filter, as you can see when you select All in the combo box, displaying all the requests. The data is filtered at the client, without sending additional requests to the XML Web service, providing the basis for a simple and efficient data model.
To | Do This |
---|---|
Use a DataSet in XML Web services | Add a method to your XML Web service that returns a DataSet. The .NET Framework automatically handles using a DataSet in XML Web services, allowing relational data to be communicated between clients and services seamlessly. |
Apply a filter to a DataSet | Use the RowFilter property of the DataView class. |
Display a DataSet using Windows Forms components | Use the DataGrid class; set the DataSource property to an instance of the DataView class to display a table from a DataSet. Many of the components include direct support for displaying a DataSet. Consult the Visual Studio .NET documentation for more details. |
3.138.135.80