Training
Certifications
Books
Special Offers
Community




 
XML Web Services in the Organization
Author Chris Boar
Pages 208
Disk N/A
Level Beg/Int
Published 06/11/2003
ISBN 9780735618824
ISBN-10 0-7356-1882-8
Price(USD) $29.99
To see this book's discounted price, select a reseller below.
 

More Information

About the Book
Table of Contents
Sample Chapter
Index
Companion Content
Related Series
Related Books
About the Author

Support: Book & CD

Rate this book
Barnes Noble Amazon Quantum Books

 


Chapter 5: Connecting with Customers



5  Connecting with Customers

This chapter focuses on connecting with customers. You might not directly deal with your customers; other companies might do so for you. But whether the relationship is direct or not, it's important to support your customers and maintain that relationship.

First we'll examine indirect customer relationships. Although another company deals with your customers for you, you still might want to provide these customers with support. Customer support can also be a great way to promote related products or services, and examples of this type of service can be found on the Internet. For example, delivery companies receive a significant amount of business by shipping products from a Web site where the customer bought a product. Indirectly, the customer has paid for delivery of the item. Often, customers can track the progress of their parcels using the delivery company's Web site. Sometimes, but not always, they can track the parcels from the site where they actually purchased the items.

You might be thinking that having users track orders through the delivery company is not really a problem. After all, most users are happy to browse to the site of the delivery company to check the status of their parcels. However, not all indirectly linked companies are so well known. The customers have placed their orders with you, so they should be able to come back to you to check on the status of their orders, including any services provided by a third party.

There are many different approaches to integrating third-party data into your own Web site, including:

  • Branding. Branding is simple but reasonably effective. The third party supplying the particular service creates a Web page matching the look and feel of your Web site. You provide your customers a link from your site to this branded page. This linkage can also be accomplished using an HTML frame, a technique that hides the URL from the customer. The disadvantage of a branded page is that you really have lost control of the page and, effectively, your customer as well.
  • Data replication. A little more complicated, data replication involves the third-party company supplying to you the necessary data that you can then present to your user. Clearly, this data needs to be kept up to date, perhaps by updating it on a nightly basis. The disadvantage of this approach is the need to replicate the data.
  • Custom data integration. Even more complicated, custom data integration involves accessing the data on the third party's servers from your own servers, possibly using a transport protocol such as HTTP. Of course, you could use other protocols such as COM or low-level packet protocols such as TCP. The disadvantage with these protocols, of course, is the requirement to allow packets to pass through any network firewalls. If you use a custom HTTP-based protocol, the disadvantage is the time and money you expend developing this solution. Each company you choose to partner with might need its own custom solution.

This is not a complete list, but it's enough to highlight the different approaches that can be taken and some of their disadvantages. Hopefully, seeing the mention of HTTP as a transport made you think of XML-based Web services as a possible solution to this problem.

Using Web Services to Support Customers

What you really want is to have pages on your Web site that access and present the data the user needs, whether that data is from your own business objects or from the business objects of your partners. Using Web services to access this data allows you to integrate with functionality on remote servers no matter what that platform is.

In the previous chapters, we have started each time by creating the Web service and then creating the client-side proxy based on the Web Services Description Language (WSDL) file from the Web service. Although this approach is the most common, clients of the Web service might need to define the interface they expect on the remote Web service. Let's look at how to define Web service interfaces.

Defining a Web Service Interface to Be Implemented by Others

The sample application for this chapter contains a few pages of the Satellite Installation Web site. These pages allow customers of the Web site to enter their personal details, select a satellite system that they want installed, and then check for an available installation appointment. For now, assume that you're technically responsible for this Web site. Your company does not actually have installation engineers of its own; instead, you contract out this part of your service to many different installation companies throughout the country. You want the service your customers receive to be quick and efficient. You need to tightly integrate your Web site to the systems of all the different installation companies. You decide to use Web services because being platform-agnostic is definitely a necessity of this solution. But even though you're going to be a client of many remote Web services, you define the interfaces they should support, which makes integrating with each remote Web service very simple. In fact, all you need to do is keep a list of each Web service's URL.

You inform each partner company (the installation providers) what interfaces they need to support by sending them a WSDL file. We've covered WSDL files previously, so they shouldn't be new to you. You could write the WSDL file manually, but in this case, it's easier to create the Web service interfaces in Microsoft Visual Studio .NET 2003 yourself and then query the service for the WSDL file. You do not need to implement each method; just define what the method should look like. For example, for the Satellite Installation company, you want the remote Web services to support three interfaces: GetAppointment, ConfirmAppointment, and CancelAppointment, as shown in the following code:

namespace InstallCompany2 {
 
    [WebService(Namespace="http://fabrikam.com/XMLWSOrg")]
    public class Install : System.Web.Services.WebService {
 
        public Install() {
        
        [WebMethod]
        public AppointmentInfo GetAppointment(customerinfo cust,
            string ordertype){
            return null;
        }
 
        [WebMethod]
        public bool ConfirmAppointment(string custref,string orderref) {
            return true;
        }
 
        [WebMethod]
        public bool CancelAppointment(string custref,string orderref){
            return true;
        }
    }
    public class customerinfo{
        public string firstname;
        public string lastname;
        public string address1;
        public string address2;
        public string city;
        public string state;
        public string zipcode;
        public string custref;
        public string orderref;
        public DateTime appointment;
    }
    public class AppointmentInfo{
        public bool Accepted;
        public DateTime AppointmentDateTime;
        public string CompanyName;
    }
}

As you can see in the preceding code, the three methods have no implementation, apart from returning the necessary data types. Now you can compile the code and then retrieve the WSDL file in a browser by entering the URL to the Web service with ?WSDL appended to the end. The WSDL file for the Satellite Installation Web site looks like this:

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"  
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:s="http://www.w3.org/2001/XMLSchema"
  xmlns:s0="http://fabrikam.com/XMLWSOrg"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
  targetNamespace="http://fabrikam.com/XMLWSOrg"
  xmlns="http://schemas.xmlsoap.org/wsdl/">
  <types>
    <s:schema elementFormDefault="qualified"
      targetNamespace="http://fabrikam.com/XMLWSOrg">
      <s:element name="GetAppointment">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="cust"
               type="s0:customerinfo" />
            <s:element minOccurs="0" maxOccurs="1" name="ordertype"
              type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="customerinfo">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="firstname"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="lastname"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="address1"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="address2"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="city"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="state"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="zipcode"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="custref"
            type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="orderref"
            type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" name="appointment"
            type="s:dateTime" />
        </s:sequence>
      </s:complexType>
      <s:element name="GetAppointmentResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1"
              name="GetAppointmentResult" type="s0:AppointmentInfo" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="AppointmentInfo">
        <s:sequence>
          <s:element minOccurs="1" maxOccurs="1" name="Accepted"
            type="s:boolean" />
          <s:element minOccurs="1" maxOccurs="1"
            name="AppointmentDateTime" type="s:dateTime" />
          <s:element minOccurs="0" maxOccurs="1" name="CompanyName"
            type="s:string" />
        </s:sequence>
      </s:complexType>
      <s:element name="ConfirmAppointment">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="custref"
              type="s:string" />
            <s:element minOccurs="0" maxOccurs="1" name="orderref"
              type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="ConfirmAppointmentResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1"
              name="ConfirmAppointmentResult" type="s:boolean" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="CancelAppointment">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="custref"
              type="s:string" />
            <s:element minOccurs="0" maxOccurs="1" name="orderref"
              type="s:string" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element name="CancelAppointmentResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1"
              name="CancelAppointmentResult" type="s:boolean" />
          </s:sequence>
        </s:complexType>
      </s:element>
      
    </s:schema>
  </types>
  <message name="GetAppointmentSoapIn">
    <part name="parameters" element="s0:GetAppointment" />
  </message>
  <message name="GetAppointmentSoapOut">
    <part name="parameters" element="s0:GetAppointmentResponse" />
  </message>
  <message name="ConfirmAppointmentSoapIn">
    <part name="parameters" element="s0:ConfirmAppointment" />
  </message>
  <message name="ConfirmAppointmentSoapOut">
    <part name="parameters" element="s0:ConfirmAppointmentResponse" />
  </message>
  <message name="CancelAppointmentSoapIn">
    <part name="parameters" element="s0:CancelAppointment" />
  </message>
  <message name="CancelAppointmentSoapOut">
    <part name="parameters" element="s0:CancelAppointmentResponse" />
  </message>
  
  <portType name="InstallSoap">
    <operation name="GetAppointment">
      <input message="s0:GetAppointmentSoapIn" />
      <output message="s0:GetAppointmentSoapOut" />
    </operation>
    <operation name="ConfirmAppointment">
      <input message="s0:ConfirmAppointmentSoapIn" />
      <output message="s0:ConfirmAppointmentSoapOut" />
    </operation>
    <operation name="CancelAppointment">
      <input message="s0:CancelAppointmentSoapIn" />
      <output message="s0:CancelAppointmentSoapOut" />
    </operation>
  </portType>
  
  <binding name="InstallSoap" type="s0:InstallSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
      style="document" />
    <operation name="GetAppointment">
      <soap:operation soapAction=
        "http://fabrikam.com/XMLWSOrg/GetAppointment" style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="ConfirmAppointment">
      <soap:operation soapAction= 
        "http://fabrikam.com/XMLWSOrg/ConfirmAppointment"
        style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <operation name="CancelAppointment">
      <soap:operation 
        soapAction="http://fabrikam.com/XMLWSOrg/CancelAppointment"
        style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
  </binding>
 
  <service name="Install">
    <port name="InstallSoap" binding="s0:InstallSoap">
      <soap:address location=
    "http://localhost/XMLWSOrg/Chapter5/InstallCompany2/install.asmx" />
    </port>
    
  </service>
</definitions>

You could describe the methods you need the remote Web services to support in other ways, such as using a simple Word document, but giving the installation companies a WSDL file has some benefits:

  • It is easy to do. Write the methods, compile the code, and then query the service for the WSDL file.
  • The WSDL file includes everything necessary. The proxy generator uses this file to build the proxy classes on a client for a remote Web service. Therefore, all the necessary information needs to be present.
  • The WSDL file can be used to verify the Web service created. If you compare the WSDL file generated by the remote Web service with this WSDL file, you should find them almost identical. The service port section, at the bottom of this WSDL file, will be different because it binds the methods to an actual URL.

Each remote installation company should use this WSDL file to implement the Web service that you'll use from within your Web site.

If you have downloaded this book's sample files, you can look at the code within the Web service of InstallCompany1 and InstallCompany2. Both solutions are located under Microsoft Press\XMLWSOrg\Chapter5. Each one implements the same WSDL file, but the actual code implemented in the methods is different. Also, notice how the names of the services differ between the two solutions. For InstallCompany1, the service is named Install.asmx, and for InstallCompany2, the service is named Service1.asmx. Although you'll need to know this URL, changing the name of the service changes only the service port section of the WSDL file, which does not cause you any problems. The code contained in Install.asmx.cs for InstallCompany1 looks like this:

namespace InstallCompany1 {
 
    [WebService(Namespace="http://fabrikam.com/XMLWSOrg")]
    public class Install : System.Web.Services.WebService {
        public Install() {
            //CODEGEN: This call is required by 
            //the ASP.NET Web Services Designer
            InitializeComponent();
        }
 
        //Component Designer generated code
        
 
        [WebMethod]
        public AppointmentInfo GetAppointment(customerinfo cust,
            string ordertype){
            if (cust.zipcode=="94101" | cust.zipcode=="94102"){
                Application.Lock();
                ArrayList activebookings;
                if (Application["ActiveBookings"]==null){
                    activebookings=new ArrayList();
                }
                else{
                    activebookings=
                        (ArrayList)Application["ActiveBookings"];
                }
                cust.appointment=new DateTime(2003,1,14,10,45,0,0);
                AppointmentInfo ai=new AppointmentInfo();
                ai.Accepted=true;
                ai.AppointmentDateTime=cust.appointment;
                ai.CompanyName="Install Company 1";
                activebookings.Add(cust);
                Application["ActiveBookings"]=activebookings;
                Application.UnLock();
                return ai;
            }
            else{
                AppointmentInfo ai=new AppointmentInfo();
                ai.Accepted=false;
                return ai;
            }
 
        }
 
        [WebMethod]
        public bool ConfirmAppointment(string custref,string orderref) {
            ArrayList activebookings=
                (ArrayList)Application["ActiveBookings"];
            for(int p=activebookings.Count-1;p==0;p--){
                customerinfo hc=(customerinfo)activebookings[p];
                if (custref==hc.custref && orderref==hc.orderref){
                    activebookings.RemoveAt(p);
                    Application.Lock();
                    Application["ActiveBookings"]=activebookings;
                    Application.UnLock();
                    //
                    // Any additional code necessary
                    // when the appointment
                    // becomes confirmed...
                    //
                }
            }
            return true;
        }
 
        [WebMethod]
        public bool CancelAppointment(string custref,string orderref){
            ArrayList activebookings=
                (ArrayList)Application["ActiveBookings"];
            for(int p=activebookings.Count-1;p==0;p--){
                customerinfo hc=(customerinfo)activebookings[p];
                if (custref==hc.custref && orderref==hc.orderref){
                    activebookings.RemoveAt(p);
                    Application.Lock();
                    Application["ActiveBookings"]=activebookings;
                    Application.UnLock();
                    //
                    // Any additional code necessary
                    // when the appointment
                    // is rejected...
                    //
                }
            }
            return true;
        }
    }
    public class customerinfo{
        public string firstname;
        public string lastname;
        public string address1;
        public string address2;
        public string city;
        public string state;
        public string zipcode;
        public string custref;
        public string orderref;
        public DateTime appointment;
    }
    public class AppointmentInfo{
        public bool Accepted;
        public DateTime AppointmentDateTime;
        public string CompanyName;
    }
}

If you compare the specification of the earlier WSDL file to the methods defined in the preceding code, you'll see that they match. The preceding code does not make a complete solution, but it's enough for our purposes. This code will always return the same appointment data.

The code within the Service1.asmx.cs file for the InstallCompany2 Web service looks like this:

namespace InstallCompany2 {
 
        [WebService(Namespace="http://fabrikam.com/XMLWSOrg")]
        public class Install : System.Web.Services.WebService {
 
        public Install() {
            //CODEGEN: This call is required by the ASP.NET
            // Web Services Designer
            InitializeComponent();
        }
 
        //Component Designer generated code
        
 
        [WebMethod]
        public AppointmentInfo GetAppointment(customerinfo cust,
            string ordertype){
            if (cust.zipcode=="98208" | cust.zipcode=="98207" | 
                cust.zipcode=="98206"){
                Application.Lock();
                ArrayList activebookings;
                if (Application["ActiveBookings"]==null){
                    activebookings=new ArrayList();
                }
                else{
                    activebookings=
                        (ArrayList)Application["ActiveBookings"];
                }
                cust.appointment=new DateTime(2003,1,14,14,30,0,0);
                AppointmentInfo ai=new AppointmentInfo();
                ai.Accepted=true;
                ai.AppointmentDateTime=cust.appointment;
                ai.CompanyName="Install Company 2";
                activebookings.Add(cust);
                Application["ActiveBookings"]=activebookings;
                Application.UnLock();
                return ai;
            }
            else{
                AppointmentInfo ai=new AppointmentInfo();
                ai.Accepted=false;
                return ai;
            }
 
        }
 
        [WebMethod]
        public bool ConfirmAppointment(string custref,string orderref) {
            ArrayList activebookings=
                (ArrayList)Application["ActiveBookings"];
            for(int p=activebookings.Count-1;p==0;p--){
                customerinfo hc=(customerinfo)activebookings[p];
                if (custref==hc.custref && orderref==hc.orderref){
                    activebookings.RemoveAt(p);
                    Application.Lock();
                    Application["ActiveBookings"]=activebookings;
                    Application.UnLock();
                    //
                    // Any additional code necessary
                    // when the appointment
                    // becomes confirmed...
                    //
                }
            }
            return true;
        }
 
        [WebMethod]
        public bool CancelAppointment(string custref,string orderref){
            ArrayList activebookings=
                (ArrayList)Application["ActiveBookings"];
            for(int p=activebookings.Count-1;p==0;p--){
                customerinfo hc=(customerinfo)activebookings[p];
                if (custref==hc.custref && orderref==hc.orderref){
                    activebookings.RemoveAt(p);
                    Application.Lock();
                    Application["ActiveBookings"]=activebookings;
                    Application.UnLock();
                    //
                    // Any additional code necessary
                    // when the appointment
                    // is rejected...
                    //
                }
            }
            return true;
        }
    }
    public class customerinfo{
        public string firstname;
        public string lastname;
        public string address1;
        public string address2;
        public string city;
        public string state;
        public string zipcode;
        public string custref;
        public string orderref;
        public DateTime appointment;
    }
    public class AppointmentInfo{
        public bool Accepted;
        public DateTime AppointmentDateTime;
        public string CompanyName;
    }
}

This code, from the Service1.asmx.cs file, looks similar to the code from the Install.asmx.cs file. The defined methods in these two files must be the same, and although a lot of the code within the methods is the same, there are differences. After all, this installation company covers a different set of postal codes. As long as the methods defined match those defined in the WSDL file, the implementations can vary. In our sample applications, these Web services are .NET based. Actually, as long as the methods defined are correct, it doesn't matter which Web service toolkit you're using to create the Web services or which platform the Web services are based on.

Examining User Interaction with the Satellite Site

Before we look at the code within the Satellite Site Web pages, we'll examine the user experience. When customers are ready to order a satellite system, they're directed to OrderPage.aspx. This page allows the user to enter personal information, such as his or her name and address, and to specify which satellite system to install. (See Figure 5-1.) This page should be secured using HTTPS because personal information is being passed over the Internet.

Click to view graphic
Click to view graphic

Figure 5-1  An example of a user completing the OrderPage.aspx page

Now the user clicks the Check Availability button. The Web site uses this information and queries the installation companies to see if they cover the area specified by the customer's address, which is done by matching the postal code. If an installation company doesn't cover the customer's address, the customer is presented with the RejectOrder.aspx page. If it covers the customer's address, the customer is presented with the ConfirmOrder.aspx page. (See Figure 5-2.) On this page, the customer can see the appointment date and time and the name of the company that will install the equipment. The customer can then confirm the order. If the order is confirmed, the customer is presented with the OrderComplete.aspx page. (See Figure 5-3.)

Click to view graphic
Click to view graphic

Figure 5-2  The ConfirmOrder.aspx page

Click to view graphic
Click to view graphic

Figure 5-3  The OrderComplete.aspx page

With an understanding of the user experience, you can now look at what's happening between the Web site and the Web services of the installation companies. Figure 5-4 is a representation of the interactions between the Web site and two installation companies. There are seven steps spread over three Web pages, as the following figure shows.

Click to view graphic
Click to view graphic

Figure 5-4  The Web site SOAP interactions

  1. When the customer clicks the Check Availability button on the OrderPage.aspx page, the code associated with the click passes the customer information to the Web service of Install Company 1.
  2. Install Company 1 receives the request and checks whether the postal code passed in the request is an area covered by the company. For the purposes of this diagram, we'll assume that the postal code is not covered by Install Company 1. The Web service sets the Accepted property of the AppointmentInformation object to False and returns the object to the Web site.
  3. The OrderPage.aspx page receives this response from Install Company 1. Because the Accepted property is False, the code makes the same call to a different company using a different URL. This dynamic binding is something we have not covered in this chapter yet. We'll look at the code in the next section.
  4. Install Company 2 receives the same customer information and checks the postal code. In this case, we'll assume that the postal code is covered by Install Company 2. The Web service gets the next available appointment date and time and builds an AppointmentInformation object with the Accepted property set to True to return to the Web site.
  5. The Web site receives the AppointmentInformation object and checks the Accepted property. Because Accepted is True, the customer is directed to the ConfirmOrder.aspx page. On this page, the appointment information is displayed and the customer can confirm the order. If the customer confirms the order, a call is made to the ConfirmAppointment Web method of the installation company that was able to accept the order.
  6. Install Company 2 receives the order confirmation and changes the appointment in its own internal records to confirmed. Although not shown in this diagram, the installation companies also support a method named CancelAppointment. If the customer does not confirm the order, CancelAppointment is called to free up the appointment that was provisionally booked.
  7. The OrderComplete.aspx page is displayed.

Setting a Web Service URL at Run Time

The OrderPage.aspx page is coded to call the same Web method of the two different remote Web services. As mentioned, the Web services for Install Company 1 and Install Company 2 both support the same Web method interfaces. Armed with this fact, the code can easily call the Web method of either company and get an expected response. To call the Web method of different Web services, you need to change the URL of the Web services at run time. You can accomplish this task by changing the Url property of the proxy instance you create. Look at the code for the Click event of the Check Availability button (Button1_Click).

private void Button1_Click(object sender, System.EventArgs e) {
    if (Page.IsValid){
        Session["FirstName"]=txtFirstName.Text;
        Session["LastName"]=txtLastName.Text;
        Session["Address1"]=txtAddress1.Text;
        Session["Address2"]=txtAddress2.Text;
        Session["City"]=txtCity.Text;
        Session["State"]=lstState.SelectedItem.Text;
        Session["ZipCode"]=txtZipCode.Text;
        if (RadioButton1.Checked){
            Session["OrderType"]="A";
        }
        else if (RadioButton2.Checked){
            Session["OrderType"]="B";
        }
        else{
            Session["OrderType"]="C";
        }
        localhost.customerinfo CustInfo=new localhost.customerinfo();
        CustInfo.firstname=txtFirstName.Text;
        CustInfo.lastname=txtLastName.Text;
        CustInfo.address1 =txtAddress1.Text;
        CustInfo.address2=txtAddress2.Text;
        CustInfo.city=txtCity.Text;
        CustInfo.state=lstState.SelectedItem.Text;
        CustInfo.zipcode=txtZipCode.Text;
        CustInfo.custref="Fabrikam";
        Application.Lock();
        int ord;
        if (Application["OrderRef"]==null){
            Application["OrderRef"]=1;
            ord=1;
        }
        else{
            ord=(int)Application["OrderRef"];
            ord++;
            Application["OrderRef"]=ord;
        }
        CustInfo.orderref=ord.ToString();
        Session["OrderRef"]=ord;
        Application.UnLock();
        localhost.Install installWS=new localhost.Install();
        String[] WebServiceURLs=new String[2];
        WebServiceURLs[0]=
            "http://localhost/XMLWSOrg/Chapter5/"+
            "InstallCompany2/Service1.asmx";
        WebServiceURLs[1]=
            "http://localhost/XMLWSOrg/Chapter5/"+
            "InstallCompany1/Install.asmx";
        bool ok=false;
        for(int p=0;p<2;p++){
            installWS.Url=WebServiceURLs[p];
            localhost.AppointmentInfo ai=installWS.GetAppointment
                (CustInfo,(string)Session["OrderType"]);
            if (ai.Accepted){
                Session["Appointment"]=ai.AppointmentDateTime;
                Session["InstallCompany"]=ai.CompanyName;
                Session["InstallURL"]=WebServiceURLs[p];
                ok=true;
                break;
            }
        }
        if (ok){
            Page.Response.Redirect("ConfirmOrder.aspx");
        }
        else{
            Page.Response.Redirect("RejectOrder.aspx");
        }
    }
}

The URL of each Web service is held in an array, making it easy to step through each URL. The line of code that sets the URL of the Web service is shown in boldface. Setting the URL in code is a very powerful and yet extremely simple capability. The preceding code can easily be changed to support many more remote Web services by increasing the size of the array that stores the URLs and changing the for loop.

Now that we've looked at indirect customer relationships, it's time to see how Web services can assist in direct customer relationships. You'll use the same basic set of Web service tools but with different clients.

Supporting Direct Business-to-Customer Relationships

How you connect directly to your customers depends on the nature of your product or service. The Microsoft software product Streets and Trips connects to a Web service to retrieve updated road construction information. A financial institution might want to allow its customers to check the value of their investments from their mobile phones. Your product might be a Global Positioning System (GPS) device that allows its users to periodically update the navigational data based on the data held on your servers.

Providing data to your customers can be as simple as creating the right Web pages on your site. But more and more these days customers are equipping themselves with mobile devices such as Personal Digital Assistants (PDAs), mobile phones, Pocket PCs, or other purpose-specific devices. Many of these devices can display Web pages, but for your customers to use your data, the device needs a connection to the Internet or to cache the page offline. You can provide your customer with a dedicated piece of software that caches your data on the local device, refreshing the data when a connection is available. This feature allows the user to benefit from your functionality even when a connection is not available. Web services are an ideal way to link from the remote device to your servers. The Microsoft Windows .NET Compact Framework allows you to use the power of .NET from either the Windows CE or the Pocket PC platform. Therefore, the great Web service functionality built into the .NET platform is available on these platforms. You might remember that allowing people to use their data anytime, anywhere, on any device is one of the original goals of .NET.

When developing a client application to be used by your customers, carefully consider which platform is most suitable. For example, an application that a user could use while shopping to track the cholesterol of items being purchased would not be helpful if it ran only on a laptop computer. It isn't easy to carry the laptop and groceries at the same time! A smart device application would be far more suitable. Also, if your application provides data that the user will want to carry out complex calculations on, a smart device would not be suitable because it is resource-limited.

Using the .NET Compact Framework

The .NET Compact Framework is a subset of the .NET Framework written to provide developers with a hardware-independent environment targeting resource-constrained devices. Visual Studio .NET 2003 integrates the .NET Compact Framework. Within Visual Studio .NET 2003, you can write applications in any installed language that target either the Pocket PC platform or Windows CE.

You can find considerable documentation on the similarities and differences between the .NET Framework and the .NET Compact Framework in Visual Studio .NET 2003. One thing to remember is that the .NET Compact Framework is targeted toward providing a rich client experience. Technologies such as ASP.NET are not part of the .NET Compact Framework. As you'll see in a moment, developing a .NET Compact Framework application in Visual Studio .NET 2003 is very similar to developing a Windows application. As you work with the different classes and controls, you might notice that not all support as much functionality as their full .NET counterparts. To make development easier, classes within the .NET Compact Framework use the same names as the equivalent classes in the .NET Framework. For example, the TreeView control exists in both the .NET Framework and the .NET Compact Framework.

Visual Studio .NET 2003 includes device emulator support. The emulator allows you to develop and test your smart device applications even if you do not have access to the actual hardware. Follow these instructions to build an application targeted for the Pocket PC platform:

  1. In Visual Studio .NET 2003, click File, point to New, and then click Project.
  2. In the New Project window, under Project Types, click either Visual Basic Projects or Visual C# Projects. Under Templates, click Smart Device Application.
  3. In the Name field, enter a suitable name and then click OK.
  4. The Smart Device Application Wizard appears. This wizard allows you to specify the target platform and the type of application you want to create.

  5. Under What Platform Do You Want To Target?, click Pocket PC.
  6. Under What Project Type Do You Want To Create?, click Windows Application and then click OK.

Now you're ready to design the user interface, add any necessary code, and then test the application. Notice the list of controls in the Toolbox is slightly less than what's available when developing Windows applications. Add a button to the form, and double-click the button to add some code to the Click event, perhaps to display a message using

MessageBox.Show("A simple message")

To test the application, follow these steps:

  1. On the Debug menu, click Start.
  2. In the Deploy SmartDeviceApplication window, make sure the emulator entry is selected (which is the default) and then click Deploy.

The emulator window will appear. The device will launch, and the .NET Compact Framework will be installed on the device. Be patient; unless the device needs to be restarted, subsequent tests take less time because the device is already loaded with the .NET Compact Framework. When the button appears on the device screen, click it. You should end up seeing something like the application shown in Figure 5-5.

Click to view graphic
Click to view graphic

Figure 5-5  A simple Pocket PC application developed with Visual Studio .NET 2003 and the .NET Compact Framework

In the previous chapters, we've seen a number of uses for Web services in either Windows applications or ASP.NET applications. For the remainder of this chapter, we'll see how easy it is to integrate Web services into a smart device application.

Implementing the Wine Guide Client

The Wine Guide application is a Pocket PC application that allows the user to view a rating of a particular wine based on votes entered by users of the system. The user is presented with a tree view where the first level is the French wine-growing regions. Within each region, the major wines are listed, and then within each wine, a number of specific vintages are listed. Figure 5-6 shows how the user interface is represented.

Click to view graphic
Click to view graphic

Figure 5-6  The user interface of the Wine Guide application

Open the Visual Studio solution for this application at Microsoft Press\XMLWSOrg\Chapter5\WineGuideClientPPC.sln. With the solution open in Visual Studio .NET 2003, you can follow these steps to demonstrate the Wine Guide functionality:

  1. On the Debug menu, click Start.
  2. In the Deploy WineGuideClientPPC window, click Pocket PC 2002 Emulator (Default) and then click Deploy.
  3. If this is the first time the Wine Guide has been run, you'll first see the Wine Guide : Set Server window. In the Server Name field, enter the name of your local machine and then click OK.
  4. The normal Wine Guide window appears. Scroll through the list, and click South West.
  5. After the wines for the South West region are downloaded, expand the South West node.
  6. Click Gaillac.
  7. When the vintages for the Gaillac region have been downloaded, expand the Gaillac wine.
  8. Now click the Gaillac Rouge 1994 Mas de Bicary. Notice the rating the wine receives and the number of votes.
  9. Select one of the five choices for the wine ranging from Bad to Great, and then click Submit Your Vote. Notice that the rating and number of votes change.

This application references a Web service named WineWebService. This reference was added in the same way references to other Web services have been made in previous applications: using the Add Web Reference tool. You can examine the Web service by opening the solution at Microsoft Press\XMLWSOrg\Chapter5\WineWebService\WineWebService.sln. The WineWebService supports the following five methods:

  • GetRegions. This method returns an array of the French wine regions.
  • GetWines. This method accepts an integer value that represents one of the wine regions and returns an array of wines for that region.
  • GetVintages. This method accepts the name of a wine and returns the vintages stored in the system for that wine.
  • GetVintageScore. This method accepts the name of a vintage and returns a complex type that includes the number of votes received for this vintage and the average of the scores from each vote.
  • SetVintageScore. This method accepts the name of one of the vintages held in the system and an integer score between 1 and 5.

In the client applications covered so far in this book, each call to a Web service has been synchronous; that is, the code makes the call to the Web service and waits for the response before continuing. In the following section, we introduce calling Web services asynchronously.

Calling Web Services Asynchronously

There are a number of reasons to call a Web service asynchronously. In the Wine Guide smart device application, the tree view is built using the results of multiple calls to a Web service. First the regions are loaded into the tree view. Then, as the user clicks on a region, the wines for that region are requested and added into the tree. If the user clicks on a wine, the vintages are then requested and added into the tree. By only requesting a part of the tree at a time, the size of the data being requested at any one time is reduced. However, more calls are made overall. When you consider that the bandwidth from the device to the Web service might be limited, calling a Web service synchronously could suspend the user interface and give the appearance of a hung application. By calling the Web service asynchronously, you can allow the application to continue responding. In this application, an activity bar is displayed until the response is received, informing the user that the application is waiting for the response. If you play with the application, you'll see that very few vintages are included in the data source (an XML file held on the server). Because the data in the file is minimal, downloading all of it at once would not be a problem. This application has been implemented this way deliberately to highlight the use of asynchronous Web service calls.

The first call made asynchronously in this client is contained in the Form1_Load procedure, as shown here:


localhost.Service1 ws=new localhost.Service1();
ws.Url="http://" + servername +  
    "/XMLWSOrg/Chapter5/WineWebService/service1.asmx";
AsyncCallback ac=new AsyncCallback(this.UpdateRegions);
ws.BeginGetRegions(ac,ws);

This code is just the part of the procedure that deals with initiating the call to the Web service method, GetRegions. The first statement creates an instance of the Web service class. The second statement sets the URL of the Web service. Normally, for examples in this book, this statement would not be necessary, but because this Web service client is running on a smart device (albeit a device emulator), the Web service needs to be referenced by the name of the machine that hosts it. The third statement creates an instance of the AsyncCallback delegate using a reference to the procedure that should be called when the Web service response is received, which is the UpdateRegions procedure in this case.

The fourth statement actually initiates the call to the Web service. When the Add Web Reference tool creates the proxy class for the Web service client, it creates three methods for each Web service method. Within this proxy class, the synchronous method is named GetRegions. The other two methods support the asynchronous use of the Web service method. The method name to start an asynchronous call is the name of the method prefixed with Begin. The method that actually gets the result of the call, used in the UpdateRegions method, is the name of the method prefixed with End. Therefore, in this case, the name of the method to start the call is BeginGetRegions. This method requires two parameters, the AsyncCallback instance and an object referred to as the asyncState, which we'll use to hold the instance of the Web service.

Now the call has been made to the Web service, and execution continues. When the response is received from the Web service, the UpdateRegions procedure will be called. This client application uses a Timer control to animate the activity bar until the response is received. The code within the UpdateRegions method looks like this:

private void UpdateRegions(IAsyncResult ar){
    localhost.Service1 ws=(localhost.Service1)ar.AsyncState;
    string[] regions=ws.EndGetRegions(ar);
    timer1.Enabled=false;
    progress.BackColor=Color.White;
 
    foreach(string reg in regions){
        TreeNode n=new TreeNode(reg);
        treeView1.Nodes.Add(n);
    }
}

The method accepts a single parameter of type IAsyncResult. The first line of code within the procedure sets up an instance of the Web service proxy class by casting the content of the ar.AsyncState property back to the proxy class. The second line of code uses the EndGetRegions method of the Web service proxy instance to get the result of the Web service call. The rest of the method adds the results into the tree view control.

The client application asynchronously calls the GetWines and GetVintages methods. Calling a Web service synchronously or asynchronously is purely a client issue; no changes are necessary to the Web service itself.

Summary

All the examples covered so far in this book support the common Web service design where many clients use a single Web service. The Web site example used earlier in this chapter is different in that it demonstrates the use of Web services with a single client and multiple Web services. Although this use is perhaps less common, it's still a valid design.

The early part of this chapter discussed using a WSDL file to help your trading partners correctly implement a Web service that you specified. Once these remote Web services are implemented, you can use the Url property of the proxy class to dynamically reference a particular remote Web service.

Using the .NET Compact Framework, you can use your knowledge of .NET to write compelling smart device applications as easily as you develop Windows or ASP.NET applications. Whether you're programming for Windows or a smart device, you can use Web services to bridge the gap between the client and remote functionality.

Within the Wine Guide application, you saw how to call a Web service method asynchronously, which is not only powerful, but also fairly simple to implement. With asynchronous calls, you can continue to have a responsive interface while possibly time-consuming tasks are accomplished in the background. You're now prepared enough to turn to the next chapter and dig into securing your Web services.



Last Updated: May 29, 2003
Top of Page