Chapter 10. State Management

In this chapter, you will learn how to:

  • Enable client sessions

  • Store and retrieve data on a per-client basis

  • Store and retrieve data on an application basis

Earlier in this book, we described the relationship between ASP.NET and XML Web services and explained that ASP.NET provides some key services that simplify the development of more complex XML Web service projects. In this chapter, we will examine ASP.NET support for state management.

By default, XML Web services created in the Microsoft .NET Framework are stateless; the service handles every client request in isolation and does not share data between requests, even those from the same client. This has not affected the design of the examples in previous chapters because we were able to treat each credit card validation request independently. We did not need to share data between client requests to validate credit card numbers.

By contrast, stateful XML Web services store data while processing a request and can use the data when processing future requests. ASP.NET provides two types of state management.

  • Client state, in which data about a particular client request is stored and used to affect the processing of future requests from that client.

  • Application state, which is not client specific. Data from any request (regardless of the client) is stored and used to affect the processing of any future request.

Client State

The network protocol used for communication between clients and XML Web services is stateless, meaning that not all of the requests from the client will be made using the same HTTP connection. ASP.NET provides a mechanism to identify clients when requests are made using several HTTP connections and to allow a programmer to store data that is unique to each identified client. These requests are grouped together as a session. ASP.NET does not create client sessions by default. You must use the EnableSession property of the WebMethod attribute to indicate which methods of the XML Web service class require client state. The following code fragments illustrate how this is done.

Example 10-1. C#

[WebMethod(EnableSession=true)]

Example 10-2. Visual Basic .NET

<WebMethod(EnableSession=True)>

When a client first makes a request to an XML Web service, ASP.NET creates a new session for the client and adds a cookie to the XML Web service response. For our purposes, a cookie is simply a piece of text that contains an identifier for the client. The client includes the cookie in any subsequent requests to the same XML Web service, enabling ASP.NET to uniquely identify the client and associate requests together; however, sessions are created only when client state is enabled via the EnableSession property of the WebMethod attribute.

When Microsoft Visual Studio .NET creates a new XML Web service project, the main service class is automatically derived from the System.Web.Services.WebService class. This base class provides you with access to client sessions via the WebService.Session property, which returns an instance of the HttpSessionState class. The HttpSessionState class (one of the .NET collection classes) can store name/value pairs that can be retrieved when the client makes subsequent requests.

Using Client Sessions

The XML Web service that we will develop to demonstrate client sessions keeps a record of the number of validation requests that a client sends to the service and the number of those requests that fail. We could not keep track of this information without client state, because we need to create data and maintain access to it over multiple client requests.

The XML Web service is based on the XML Web service example from the section "Using Custom Fault Codes" in Chapter 7. We will define a new data type to represent the client request statistics.

Procedure 10-1. Create the Project

  1. Open the project that you created in Chapter 7. The URL for that project should be http://localhost/XMLWebServices/Chapter07/CustomFaultCodes/ValidatorService.

  2. Select Copy Project from the Project menu, and then copy the project to http://localhost/XMLWebServices/Chapter10/StateManagement/ValidatorService.

  3. Open the new project in Visual Studio .NET, and then save the solution file for the new project.

Procedure 10-2. Create the New Data Type

  1. In Solution Explorer, right-click on the ValidatorService project item (highlighted in bold) and select Add followed by Add Class from the shortcut menu. Type ValidationState into the Name text box, and then click Open.

    This step creates a new Microsoft Visual Basic .NET or C# class file; an entry will appear in Solution Explorer, named ValidationState.cs or ValidationState.vb depending on the language you are using.

  2. Right-click on the ValidationState.cs or ValidationState.vb file in Solution Explorer, and then select View Code from the shortcut menu.

  3. Delete all of the code in the editor window, and replace it with the following code.

    This code defines the ClientStats class, which we will use to track the total number of requests made by a client and how many of those requests were for invalid card numbers.

    Example 10-3. C#

    namespace ValidatorService {
    
        public class ClientStats {
            public int o_total_requests;
            public int o_failed_validations;
        }
    }

    Example 10-4. Visual Basic .NET

    Public Class ClientStats
        Public o_total_requests As Integer
        Public o_failed_validations As Integer
    End Class

Procedure 10-3. Update the XML Web Service Class

  1. In Solution Explorer, right-click on the Validation.asmx file and select View Code from the shortcut menu.

    This opens the code-behind file (either Validation.asmx.cs or Validation.asmx.vb depending on the language you are using).

  2. Add the following statements to the code file to define the GetClientStatistics method.

    This method demonstrates the basic functionality of the Session object, which is to store name/value pairs for each client. The first line of the method body attempts to retrieve the ClientStats object for the client that has invoked this method and creates one if needed.

    The first line highlighted in bold demonstrates how to retrieve a name/value pair from the Session object; the second bold statement shows how to add a new name/value pair. (If you have used the classes from the System.Collections namespace, you will notice that adding data to and retrieving data from the Session object is similar to the way that the Hashtable collection class is used.) Notice that we do not have to worry about which client we are dealing with. ASP.NET automatically ensures that the Session object is working with the correct set of data. We store an instance of the ClientStats class, using the name clientstats.

    Example 10-5. C#

    [WebMethod(EnableSession=true)]
    public ClientStats GetClientStatistics() {
        ClientStats x_stats = (ClientStats)Session["clientstats"];
        if (x_stats == null) {
            // we need to create a new ClientStats object for this
            // session; typically because this is the first request
            // that the client has made and there is no prior state
            // information available in the session.
            x_stats = new ClientStats();
            Session["clientstats"] =  x_stats;
        }
        return x_stats;
    }

    Example 10-6. Visual Basic .NET

    <WebMethod(EnableSession:=True)> _
    Public Function GetClientStatistics() As ClientStats
        Dim x_stats As ClientStats
        If Session("clientstats") Is Nothing Then
            ’ we need to create a new ClientStats object for this
            ’ session; typically because this is the first request
            ’ that the client has made and there is no prior state
            ’ information available in the session.
            x_stats = New ClientStats()
            Session("clientstats") = x_stats
        Else
            x_stats = CType(Session("clientstats"), ClientStats)
        End If
        Return x_stats
    End Function
  3. Replace the existing ValidateCard method with the following code.

    The differences between this method and the version in the example from Chapter 7 are shown in boldface. Notice that we have modified the WebMethod attribute declaration to enable client sessions. The other changes to the ValidateCard method update the running statistics using the ClientStats class that we defined in the previous section. We have encapsulated all of the statements that deal with the Session object in the method that we created in the previous step; the modifications to the ValidateCard method ensure that the statistics are maintained for the client. We have added the finally clause to the try statement to ensure that we record requests that result in an exception being thrown by the CreditCardValidator.dll library. Otherwise, the ValidateCard method would return as soon as the SoapException was thrown, and we would not be able to update our statistics correctly.

    Example 10-7. C#

    [WebMethod(EnableSession=true)]
    public void ValidateCard(ref ValidationObject p_object) {
        // define a boolean to hold the result
        // of the validation request.
        bool x_result = false;
    
        // define a boolean to indicate if the
        // validation request resulted in an
        // exception being thrown by the 
        // CreditCardValidator.dll library
        bool x_error = false;
    
        try {
            // create the validator instance that we will
            // use to check the card number
            Validator x_validator = new Validator();
    
            // use the type of card supplied in the 
            // validation object to determine which
            // library method we should call in order
            // to perform the validation.
            switch (p_object.o_card_type) {
                case CARD_TYPE.AMEX:
                    // the client has requested that we validate
                    // an AMEX card number
                    x_result = x_validator.ValidateAMEX(
                        p_object.o_card_number);
                    break;
                case CARD_TYPE.MASTERCARD:
                    // the client has requested that we validate
                    // an MasterCard card number
                    x_result = x_validator.ValidateMasterCard(
                        p_object.o_card_number);
                    break;
                case CARD_TYPE.VISA:
                    // the client has requested that we validate
                    // an VISAcard number
                    x_result = x_validator.ValidateVisa(
                        p_object.o_card_number);
                    break;
                default:
                    // the method should not reach this point; if it
                    // does, it is because the client has specified
                    // a value which is not contained in the CARD_TYPE
                    // enumeration. Should this happen, the client has 
                    // requested that we validate a card type that we 
                    // do not support.
                    x_result = false;
                    break;
            }
    
        } catch (ApplicationException x_ex) {
            // update the variable to indicate 
            // that an exception has been thrown by
            // CreditCardValidator.dll library
            x_error = true;
        
            // return a soap exception with a custom fault 
            // code based on the type of the application 
            // exception
            throw GetException(x_ex);
    
        } finally {
            // get the status object for this client
            ClientStats x_stats = GetClientStatistics();
            // increment the counter for the total
            // number of requests
            x_stats.o_total_requests++;
            // if the result was not true, or if an
            // exception was thrown, then update the
            // failed request counter
            if (x_error || !x_result) {
                x_stats.o_failed_validations++;
            }
    
            // set the result status in the validation object
            p_object.o_valid = x_result;
        }
    }

    Example 10-8. Visual Basic .NET

    <WebMethod(EnableSession:=True)> _
    Public Sub ValidateCard(ByRef p_object As ValidationObject)
        ’ define a boolean to hold the result
        ’ of the validation request
        Dim x_result As Boolean
    
        ’ define a boolean to indicate if the
        ’ validation request resulted in an
        ’ exception being thrown by the 
        ’ CreditCardValidator.dll library
        Dim x_error As Boolean = False
    
        Try
            ’ create the validator instance that we will
            ’ use to check the card number
            Dim x_validator As Validator = New Validator()
    
            ’ use the type of card supplied in the 
            ’ validation object to determine which
            ’ library method we should call in order
            ’ to perform the validation.
            Select Case p_object.o_card_type
                Case CARD_TYPE.AMEX
                    ’ the client has requested that we validate
                    ’ an AMEX card number
                    x_result = x_validator.ValidateAMEX( _
                        p_object.o_card_number)
                Case CARD_TYPE.MASTERCARD
                    ’ the client has requested that we validate
                    ’ an MasterCard card number
                    x_result = x_validator.ValidateMasterCard( _
                        p_object.o_card_number)
                Case CARD_TYPE.VISA
                    ’ the client has requested that we validate
                    ’ an VISAcard number
                    x_result = x_validator.ValidateVisa( _
                        p_object.o_card_number)
                Case Else
                    ’ the method should not reach this point; if it
                    ’ does, it is because the client has specified
                    ’ a value which is not contained in the CARD_TYPE
                    ’ enumeration. Should this happen, the client has 
                    ’ requested that we validate a card type that we 
                    ’ do not support.
                    x_result = False
            End Select
        Catch x_ex As ApplicationException
            ’ update the variable to indicate 
            ’ that an exception has been thrown by
            ’ CreditCardValidator.dll library
            x_error = True
    
            ’ return a soap exception with a custom fault 
            ’ code based on  the type of the application 
            ’ exception
            Throw GetException(x_ex)
        Finally
            ’ get the status object for this client
            Dim x_stats As ClientStats = GetClientStatistics()
            ’ increment the counter for the total
            ’ number of requests
            x_stats.o_total_requests = x_stats.o_total_requests + 1
    
            ’ if the result was not true, or if an
            ’ exception was thrown, then update the
            ’ failed request counter
            If x_error Or Not x_result Then
                x_stats.o_failed_validations _
                    = x_stats.o_failed_validations + 1
            End If
    
            ’ set the result status in the validation object
            p_object.o_valid = x_result
        End Try
    End Sub
  4. Build the project by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.

Consuming an XML Web Service That Uses Client State

By default, support for cookies is disabled in the XML Web service proxy class that is created by Visual Studio .NET. This means that the proxy class will not send requests that include the unique identifier assigned to the client by ASP.NET. To enable cookie support (and therefore client session state), the CookieContainer property of the proxy class must be set to a new instance of the System.Net.CookieContainer class. The identifier assigned by ASP.NET is only valid for one instance of the proxy class. ASP.NET will identify a new client each time a new proxy class is created and the CookieContainer property is set.

In this section, we will build a Windows Forms client that makes use of the GetClientStatistics Web method that we created in the example from the previous section. Unlike the clients we created in earlier chapters, the ValidatorService proxy class we create in this chapter will be an instance variable, meaning that a single instance of the class will be used to send all requests to the XML Web service. Previously, we created a new proxy class for each request. By defining the proxy as an instance variable and setting the CookieContainer property when it is created, all of the requests the client sends to the XML Web service will use the same client identifier and be considered a single session by the XML Web service.

Procedure 10-4. Create the Windows Form

  1. Copy the client template project to C:XMLWebServices­SBSProjectsChapter10StateManagement as described in the sidebar "XML Web Service Client Projects" in Chapter 7. Change the Size property of Form1 to 288, 272.

  2. Add the following controls to Form1, and 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:

    Create the Windows Form

Procedure 10-5. Add the Web Reference

  1. 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/Chapter10/StateManagement/ValidatorService/Validation.asmx . (For details about adding a Web reference, see the section "Creating a Web Reference" in Chapter 4.)

  2. In Solution Explorer, right-click the Web reference and select Rename from the shortcut menu. Rename the Web reference Validator.

Procedure 10-6. Edit the Code File

  1. 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.

  2. 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 10-9. 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 10-10. 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
  3. 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.

    Example 10-11. C#

    public class Form1 : System.Windows.Forms.Form
    {
        // define the proxy class that we will use to 
        // consume the XML Web service
        private ValidatorService o_service;

    Example 10-12. Visual Basic .NET

    Public Class Form1 _
        Inherits System.Windows.Forms.Form
    
        ’ define the proxy class that we will use to 
        ’ consume the XML Web service
        Private o_service As ValidatorService
  4. 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.

    Visual Basic .NET

    Example 10-13. 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 10-14. 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
  5. Build the solution by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.

Procedure 10-7. Test the XML Web Service

  1. Select Start Without Debugging from the Debug menu (or press Ctrl+F5) to start the client.

  2. Enter the credit card number 4921 8352 2155 2042 into the client, and then click the Validate button.

    The client will request that the ValidatorService XML Web service validate the credit card number and then obtain the request statistics, which are displayed at the bottom of the client window, as shown in the following screen shot. Because you have entered a valid card number, the statistics report that you have made one request and that no invalid requests have been sent.

    Test the XML Web Service
  3. Enter baddata as the card number, and then click the Validate button.

    The request statistics will now report a total of two requests, one of which was invalid. Remember that the client does not keep a record of the requests; the statistical record is obtained afresh from the XML Web service each time that the Validate button is clicked.

This example provides a simple demonstration of the facility to store name/value pairs in the Session object and the support provided by ASP.NET to identify clients even when requests are made using multiple network connections. Without this support, our example would not have worked and would have returned a new instance of the ClientStats object for every request. Instead, ASP.NET recognized that the two requests came from the same client and that they therefore should be associated together as part of the same client session.

Application State

In the examples in the chapters so far, you’ve created a C# or Visual Basic .NET class that contains methods that are exposed as the methods of an XML Web service. What we have not mentioned up to now is that a new instance of this class is created for each request from a client, even if that client has made requests previously. Creating a new instance for each request provides the benefits of scalability, performance, and security. This fact did not affect the design of the examples in previous chapters because each client request was self-contained; the client requested that a card number be validated and each client request was processed in isolation. Subsequent requests from the same client or from other clients were treated independently and shared no common data.

One effect of this approach is that it prevents you from using variables to maintain data values. No values assigned to variables can be shared between client requests because a new instance of the XML Web service class is created every time a Web method is invoked.

Support for application state is not enabled as with client session state; the Application property is always available for any XML Web service when the .asmx code-behind file is derived from the WebService class, including all XML Web service projects created by Visual Studio .NET. The Application property returns an instance of the HttpApplicationState class, which can be used to store name/value pairs that are accessible during requests from any client.

Using the Application Property

To demonstrate the features of the Application property, we will extend the XML Web service created earlier, in the section "Using Client Sessions," to maintain a list of the validation requests received by the XML Web service. The list will reflect requests made by all clients of the service. To expose the request history, we will define a new method named GetValidationHistory, which will return a string containing details of each request that has been made, one to a line.

Procedure 10-8. Modify the XML Web Service Class

  1. Open the XML Web service project that you created in the section "Using Client Sessions." The URL for this project should be http://localhost/XMLWebServices/Chapter10/StateManagement/ValidatorService.

  2. In Solution Explorer, right-click on the Validation.asmx file and select View Code from the shortcut menu to open the code-behind file (either Validation.asmx.cs or Validation.asmx.vb, depending on the language you are using).

  3. Add the following statements to the finally clause of the catch statement in the ValidateCard method. The statements that you need to add are marked in bold.

    When a client request is processed by the ValidateCard method, we attempt to retrieve an ArrayList through the Application property by using the name ValidationObjects. We use this list to build the validation request history. The ArrayList will not exist for the first request received after IIS loads the XML Web service, so we take care to create it as needed. Once we have the ArrayList, we add the ValidationObject received from the client so that we have a record of the type of card, the card number, and the result indicating the validity of the card number.

    Example 10-15. C#

    } finally {
        // get the status object for this client
        ClientStats x_stats = GetClientStatistics();
        // increment the counter for the total
        // number of requests
        x_stats.o_total_requests++;
        // if the result was not true, or if an
        // exception was thrown, then update the
        // failed request counter
        if (x_error || !x_result) {
            x_stats.o_failed_validations++;
        }
    
        // set the result status in the validation object
        p_object.o_valid = x_result;
    
        // update the array list held in the application
        // state to reflect the new request
        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;
        }
        x_list.Add(p_object);
    }

    Example 10-16. Visual Basic .NET

    Finally
        ’ get the status object for this client
        Dim x_stats As ClientStats = GetClientStatistics()
        ’ increment the counter for the total
        ’ number of requests
        x_stats.o_total_requests = x_stats.o_total_requests + 1
    
        ’ if the result was not true, or if an
        ’ exception was thrown, then update the
        ’ failed request counter
        If x_error Or Not x_result Then
            x_stats.o_failed_validations _
                = x_stats.o_failed_validations + 1
        End If
    
        ’ set the result status in the validation object
        p_object.o_valid = x_result
    
        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 
        ’ 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
        ’ update the array list held in the application
        ’ state to reflect the new request
        x_list.Add(p_object)
    
    End Try
  4. Add the following statements to the code file to define the GetValidationHistory method.

    When invoked, this method iterates through the list of ValidationObject instances and uses the StringBuilder class to construct a multiline String that contains the details of one validation request per line.

    Example 10-17. C#

    [WebMethod]
    public string GetValidationHistory() {
        
        // generate the information to be placed in the cache
        // create the string builder, which we will use
        // to construct the response to the client
        StringBuilder x_builder = new StringBuilder();
    
        // 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;
        }
    
        // run through the list and build up the contents
        // of the StringBuilder to contain one summary line
        // for each of the validation requests we have processed
        foreach (ValidationObject x_object in x_list) {
            x_builder.Append(x_object.o_card_type + " " 
                + x_object.o_card_number + " " 
                + x_object.o_valid + "
    ");
        }
    
        // update the local reference to the history data so
        return x_builder.ToString();   
    }

    Example 10-18. Visual Basic .NET

    <WebMethod()> _
    Public Function GetValidationHistory() As String
    
        ’ generate the information to be placed in the cache
        ’ create the string builder, which we will use
        ’ to construct the response to the client
        Dim x_builder As StringBuilder = New StringBuilder()
    
        ’ 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
        ’ 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
    
        ’ run through the list and build up the contents
        ’ of the StringBuilder to contain one summary line
        ’ for each of the validation requests we have processed
        Dim x_object As ValidationObject
        For Each x_object In x_list
            x_builder.Append(String.Concat(x_object.o_card_type, " ", _
                x_object.o_card_number, " ", x_object.o_valid, "
    "))
        Next
    
        ’ update the local reference to the history data so
        Return x_builder.ToString()
    End Function
  5. Scroll to the top of the Form1 code view, and then add the following statement to import the System.Text namespace. This gives the code access to the StringBuilder class.

    Example 10-19. C#

    // the following statement allows us to use
    // the string builder class
    using System.Text;

    Example 10-20. Visual Basic .NET

    ‘ the following statement allows us to use
    ‘ the string builder class
    Imports System.Text
  6. Build the solution by selecting Build Solution from the Build menu or pressing Ctrl+Shift+B.

Building the Request History Client

We can use the client we built in the section "Consuming an XML Web Service That Uses Client State" to make validation requests of the XML Web service, but we need a different client to obtain and display the history of those requests. In this section, we will create a new Web Forms application that will show the list of requests.

Once we have the WebFormsClient project structure in place, we’ll need to create the user interface. We’ll build a simple interface with a text box to display the request history and a button whose Click event will cause the history to be obtained from the XML Web service. Don’t worry about getting the size and position of the components exact; just be sure that the result looks something like WebForm1 shown here:

Building the Request History Client

Procedure 10-9. Create the Project

  1. Create a Visual Studio .NET project using the C# or Visual Basic .NET ASP.NET Web Application template. Use the URL http://localhost/XMLWebServices/Chapter10/StateManagement/WebFormsClient for this example.

  2. 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/Chapter10/StateManagement/ValidatorService/Validation.asmx .

  3. Rename the Web reference Validator.

Procedure 10-10. Create the Web Form

  1. Double-click the WebForm1.aspx entry in Solution Explorer to open WebForm1 in design view.

  2. Press F4 to open the property page for the WebForm1 DOCUMENT element.

  3. Change the Title property of DOCUMENT to Validation History Client.

  4. Add the Web controls listed in the following table to WebForm1, and configure their properties with the values specified.

    Control

    Property

    Value

    TextBox

    ReadOnly

    True

     

    Rows

    20

     

    TextMode

    MultiLine

     

    Height

    192px

     

    Width

    406px

     

    ID

    HistoryTextBox

    Button

    Text

    Get History

     

    Height

    29px

     

    Width

    141px

     

    ID

    HistoryButton

Procedure 10-11. Implement the WebFormsClient Functionality

  1. In the WebForm1 design view, double-click on the Get History button.

    Visual Studio .NET switches WebForm1 to code view and displays an empty method named HistoryButton_Click. Changing this method allows us to control what happens when the user clicks the Get History button.

  2. Add the following code to the method.

    When the button is clicked, the statement you’ll add to the HistoryButton_Click method will request the history string from the XML Web service and display it in the text box. Notice that we replace any existing text with the new history String; the entire request history is returned from the XML Web service each time the GetValidationHistory method is invoked.

    Example 10-21. C#

    private void HistoryButton_Click(object sender, System.EventArgs e)
    {
        ValidatorService x_service = new ValidatorService();
        HistoryTextBox.Text = x_service.GetValidationHistory();
    }

    Example 10-22. Visual Basic .NET

    Private  Sub HistoryButton_Click(ByVal sender As Object, ByVal e _
        As System.EventArgs) Handles HistoryButton.Click
        Dim x_service As ValidatorService =  New ValidatorService() 
        HistoryTextBox.Text = x_service.GetValidationHistory()
    End Sub
  3. Scroll to the top of the WebForm1 code view, and then add the following statement to import the namespace of the proxy class.

    Example 10-23. C#

    // imports the webservice proxy namespace
    using WebFormsClient.Validator;

    Example 10-24. Visual Basic .NET

    ‘imports the webservice proxy namespace
    Imports WebFormsClient.Validator
  4. Press Ctrl+Shift+B, or select Build Solution from the Build menu to build the solution.

Testing the Application State Support

We now have three components: an XML Web service that maintains a history of client validation requests, a Windows Forms client that makes the validation requests, and a Web Forms client that uses the new GetValidationHistory Web method to obtain a list of the requests made by all clients. We will now generate a small number of validation requests and use the Web Forms client to display the list.

Procedure 10-12. Generate the Validation Requests

  1. Start the client application.

    Using Windows Explorer, execute the Windows Forms client application we created earlier in this chapter. C# programmers can find the file at C:XMLWebServicesSBSProjectsChapter10StateManagement WindowsFormsClientindebugWindowsFormsClient.exe. Visual Basic .NET programmers will find the file at C:XMLWebServicesSBSProjectsChapter10 StateManagementWindowsFormsClientinWindowsFormsClient.exe.

  2. Check that the VISA item is selected as the card type, and then enter the number 4921 8352 2155 2042 as the card number.

  3. Click the Validate button.

  4. Select the AMEX item as the card type, and then enter baddata as the card number.

  5. Click the Validate Button.

  6. Select the MasterCard item as the card type, and enter 1234 as the card number.

  7. Click the Validate Button.

Procedure 10-13. View the Validation Request History

  1. Press F5; Visual Studio .NET launches Microsoft Internet Explorer and loads the WebFormsClient page.

  2. Click the Get History button.

    As you can see in the following, the text box displays a list of the card types and numbers that we entered above, as well as a Boolean representing the validity of the card number. Depending on what operating system you are running, your display might look slightly different.

    View the Validation Request History

Chapter 10 Quick Reference

To

Do This

Enable client sessions

Apply the following attribute declaration to the methods of your XML Web service class. If you are using C#, the declaration is [WebMethod(EnableSession=true)]. In Visual Basic .NET, the declaration is <WebMethod(EnableSession=True)>

Store or retrieve name/value pairs that are private to each client

Use the Session property of the WebService class.

Enable application state

Application state is always enabled.

Store or retrieve name/value pairs that are shared between all clients

Use the Application property of the WebService class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.4.181