8.                        Chapter 8 Interface-Based Web Service Development

Interface-based programming was popularized with component-based development in the 90’s. Using technologies like COM, you could define an interface then have several components that implement it. Clients could then utilize any of those components by programming against the interface. As your Web services evolve and mature, you will find the need to factor out Web service methods into interfaces, implement existing standard interfaces on your Web services, and program clients against an interface rather than a specific Web service. Interfaces can also be useful for versioning Web services by leaving the old interface intact and implementing a new interface on the same service.

WSDL bindings make all this possible. In Chapter 4 you learned all about WSDL bindings and how they define a concrete set of operations and provide the information needed to invoke those operations. A Web service then implements one or more binding and exposes them at a particular location defined by the port. If you haven’t read Chapter 4, you can still read this chapter and learn how to do interface-based programming. However, you will get a lot more out of this chapter if you first read Chapter 4.

Defining Interfaces

The first step in interface-based programming is to define the interfaces you want to implement. In my opinion, when you build a Web service you should always start with defining the interface. Today, tools like Visual Studio .NET do not provide direct support for that. I am hopeful that future versions of those tools will provide the needed support for defining Web service interfaces.

Although you can use notepad to create a WSDL document from scratch, you’ll probably want a more productive and less error-prone way to define your interfaces. An easy way to define a Web service interface is to create a Web service and define the Web methods you want the interface to have. If you have parameters with complex types, you define those types in schemas first then use xsd.exe to generate classes from the schemas (see chapter 2 for more information on xsd.exe).

By default, all of a Web service’s methods belong to the same binding and that binding (i.e. interface) has the same name as the Web service class with the word Soap appended. If you’ve created COM components in Visual Basic, you may know that each component you create has a default interface which is given the name _ClassName so the concept of auto-generated interfaces shouldn’t be new to you.

To control the binding’s name and namespace, you use the WebServiceBinding attribute on the Web service class and specify that binding’s name and namespace. Then on each Web method that the service exposes, you add SoapDocumentMethod or SoapRpcMethod and set its Binding property to the binding name. Listing 8-1 shows an example class called SupplierIface1 which exposes its methods in a binding called ISupplier.

 

Listing 8‑1 A Web service example which exposes a binding called ISupplier. (VBWSBook\Chapter8\Supplier1.asmx.vb)

Namespace Supplier1

    Public Structure Order

        Public CustomerEmail As String

        Public ShipVia As Shipper

        Public ShipName As String

        Public ShipAddress As String

        Public ShipCity As String

        Public ShipState As String

        Public ShipZipCode As String

        Public OrderItems() As OrderItem 'array of OrderItems

    End Structure

    Public Structure OrderItem

        Public ProductID As Integer

        Public Quantity As Integer

    End Structure

    Public Enum Shipper

        FedEx = 1

        UPS

        USPS

    End Enum

    Public Enum OrderStatus

        Pending

        Shipped

        Delivered

    End Enum

    Public Structure OrderInfo

        Public Status As OrderStatus

        Public ShippingType As String

        Public DeliveredDate As Date

        Public DeliveredTo As String

    End Structure

    Public Structure QuoteInfo

        Public ProductCost As Double

        Public Tax As Double

        Public Shipping As Double

        Public TotalCost As Double

    End Structure

    <WebServiceBinding( _

    Name:="ISupplier", _

    [Namespace]:="http://LearnXmlWS.com/Supplier"), _

    WebService([Namespace]:="http://LearnXmlWS.com/Supplier", _

     Description:="The supplier's Web service")> _

    Public Class SupplierIface1

        Inherits System.Web.Services.WebService

        <WebMethod( _

                                                Description:= _

                                                "Places the order then returns the new order id"), _

        SoapDocumentMethod(Binding:="ISupplier")> _

        Public Function PlaceOrder(ByVal newOrder As Order) As String

            'returns a new order id

        End Function

        <WebMethod(), _

        SoapDocumentMethod(Binding:="ISupplier")> _

        Public Function CheckStatus(ByVal OrderId As String) As OrderInfo

            'returns an orderinfo structure

        End Function

        <WebMethod(), _

        SoapDocumentMethod(Binding:="ISupplier")> _

        Public Function GetPriceQuote(ByVal newOrder As Order) As QuoteInfo

            'returns an orderinfo structure

        End Function

 

    End Class

End Namespace

The first part of listing 8-1 defines the data types that will be used by the service methods, e.g. Order, OrderItem, Shipper, OrderStatus, OrderInfo, QuoteInfo. The SupplierIface1 class has two attributes applied to it. WebServiceBinding has its name property set to ISupplier and its namespace property set to http://LearnXmlWS.com/Supplier. Each of the Web service methods has a SoapDocumentMethod applied to it with the Binding property set to ISupplier making the methods part of the interface called ISupplier. The resulting WSDL document contains the binding definition as well as the service definition.

To get a pure interface definition, you can save this WSDL document to disk and remove the <service> element which specifies a particular implementation for the interface. The edited WSDL document, shown in listing 8-2 now contains your interface definition which you can give to other developers who can then use it to implement the same binding (i.e. interface) on their services.

 

Listing 8‑2 The interface WSDL after removing the <service> element (VBWSBook\Chapter8\SingleInterface.wsdl)

<?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://LearnXmlWS.com/Supplier" 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://LearnXmlWS.com/Supplier" xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>

    <s:schema elementFormDefault="qualified" targetNamespace="http://LearnXmlWS.com/Supplier">

      <s:element name="PlaceOrder">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="1" maxOccurs="1" name="newOrder" type="s0:Order" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:complexType name="Order">

        <s:sequence>

          <s:element minOccurs="0" maxOccurs="1" name="CustomerEmail" type="s:string" />

          <s:element minOccurs="1" maxOccurs="1" name="ShipVia" type="s0:Shipper" />

          <s:element minOccurs="0" maxOccurs="1" name="ShipName" type="s:string" />

          <s:element minOccurs="0" maxOccurs="1" name="ShipAddress"

   type="s:string" />

          <s:element minOccurs="0" maxOccurs="1" name="ShipCity" type="s:string" />

          <s:element minOccurs="0" maxOccurs="1" name="ShipState" type="s:string" />

          <s:element minOccurs="0" maxOccurs="1" name="ShipZipCode" type="s:string" />

          <s:element minOccurs="0" maxOccurs="1" name="OrderItems" type="s0:ArrayOfOrderItem" />

        </s:sequence>

      </s:complexType>

      <s:simpleType name="Shipper">

        <s:restriction base="s:string">

          <s:enumeration value="FedEx" />

          <s:enumeration value="UPS" />

          <s:enumeration value="USPS" />

        </s:restriction>

      </s:simpleType>

      <s:complexType name="ArrayOfOrderItem">

        <s:sequence>

          <s:element minOccurs="0" maxOccurs="unbounded" name="OrderItem" type="s0:OrderItem" />

        </s:sequence>

      </s:complexType>

      <s:complexType name="OrderItem">

        <s:sequence>

          <s:element minOccurs="1" maxOccurs="1" name="ProductID" type="s:int" />

          <s:element minOccurs="1" maxOccurs="1" name="Quantity" type="s:int" />

        </s:sequence>

      </s:complexType>

      <s:element name="PlaceOrderResponse">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="0" maxOccurs="1" name="PlaceOrderResult" type="s:string" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:element name="CheckStatus">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="0" maxOccurs="1" name="OrderId" type="s:string" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:element name="CheckStatusResponse">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="1" maxOccurs="1" name="CheckStatusResult" type="s0:OrderInfo" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:complexType name="OrderInfo">

        <s:sequence>

          <s:element minOccurs="1" maxOccurs="1" name="Status" type="s0:OrderStatus" />

          <s:element minOccurs="0" maxOccurs="1" name="ShippingType" type="s:string" />

          <s:element minOccurs="1" maxOccurs="1" name="DeliveredDate" type="s:dateTime" />

          <s:element minOccurs="0" maxOccurs="1" name="DeliveredTo" type="s:string" />

        </s:sequence>

      </s:complexType>

      <s:simpleType name="OrderStatus">

        <s:restriction base="s:string">

          <s:enumeration value="Pending" />

          <s:enumeration value="Shipped" />

          <s:enumeration value="Delivered" />

        </s:restriction>

      </s:simpleType>

      <s:element name="GetPriceQuote">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="1" maxOccurs="1" name="newOrder" type="s0:Order" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:element name="GetPriceQuoteResponse">

        <s:complexType>

          <s:sequence>

            <s:element minOccurs="1" maxOccurs="1" name="GetPriceQuoteResult" type="s0:QuoteInfo" />

          </s:sequence>

        </s:complexType>

      </s:element>

      <s:complexType name="QuoteInfo">

        <s:sequence>

          <s:element minOccurs="1" maxOccurs="1" name="ProductCost" type="s:double" />

          <s:element minOccurs="1" maxOccurs="1" name="Tax" type="s:double" />

          <s:element minOccurs="1" maxOccurs="1" name="Shipping" type="s:double" />

          <s:element minOccurs="1" maxOccurs="1" name="TotalCost" type="s:double" />

        </s:sequence>

      </s:complexType>

    </s:schema>

  </types>

  <message name="PlaceOrderSoapIn">

    <part name="parameters" element="s0:PlaceOrder" />

  </message>

  <message name="PlaceOrderSoapOut">

    <part name="parameters" element="s0:PlaceOrderResponse" />

  </message>

  <message name="CheckStatusSoapIn">

    <part name="parameters" element="s0:CheckStatus" />

  </message>

  <message name="CheckStatusSoapOut">

    <part name="parameters" element="s0:CheckStatusResponse" />

  </message>

  <message name="GetPriceQuoteSoapIn">

    <part name="parameters" element="s0:GetPriceQuote" />

  </message>

  <message name="GetPriceQuoteSoapOut">

    <part name="parameters" element="s0:GetPriceQuoteResponse" />

  </message>

  <portType name="ISupplier">

    <operation name="PlaceOrder">

      <documentation>Places the order then returns the new order id</documentation>

      <input message="s0:PlaceOrderSoapIn" />

      <output message="s0:PlaceOrderSoapOut" />

    </operation>

    <operation name="CheckStatus">

      <input message="s0:CheckStatusSoapIn" />

      <output message="s0:CheckStatusSoapOut" />

    </operation>

    <operation name="GetPriceQuote">

      <input message="s0:GetPriceQuoteSoapIn" />

      <output message="s0:GetPriceQuoteSoapOut" />

    </operation>

  </portType>

  <binding name="ISupplier" type="s0:ISupplier">

    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

    <operation name="PlaceOrder">

      <soap:operation soapAction="http://LearnXmlWS.com/Supplier/PlaceOrder" style="document" />

      <input>

        <soap:body use="literal" />

      </input>

      <output>

        <soap:body use="literal" />

      </output>

    </operation>

    <operation name="CheckStatus">

      <soap:operation soapAction="http://LearnXmlWS.com/Supplier/CheckStatus" style="document" />

      <input>

        <soap:body use="literal" />

      </input>

      <output>

        <soap:body use="literal" />

      </output>

    </operation>

    <operation name="GetPriceQuote">

      <soap:operation soapAction="http://LearnXmlWS.com/Supplier/GetPriceQuote" style="document" />

      <input>

        <soap:body use="literal" />

      </input>

      <output>

        <soap:body use="literal" />

      </output>

    </operation>

  </binding>

</definitions>

 

Implementing an Interface

Whether you define the interfaces yourself or work with interfaces that someone else defined, you’ll eventually want to implement those interfaces. Although you could read the WSDL document and write all the corresponding VB code from scratch including the WebServiceBinding attribute, something tells me you’re not going to want to do that. Instead, you can use wsdl.exe with the /server switch to tell it you want to create a service that implements the specified interface. wsdl.exe takes the WSDL document’s URL, the language to use for generated code, and the output file name:

 

wsdl.exe /server http://VBWSServer/vbwsbook/Chapter8/SingleInterface.wsdl /l:VB /out:CSupplier.vb

 

Listing 8-3 shows the inetersting part of the resulting code in CSupplier.vb.

 

Listing 8‑3 A Web service implementation generated by wsdl.exe when using /server switch. (VBWSBook\Chapter8\InterfaceImpl\CSupplier.vb)

'

'This source code was auto-generated by wsdl

'

<WebServiceBindingAttribute(Name:="ISupplier", _

[Namespace]:="http://LearnXmlWS.com/Supplier")> _

Public MustInherit Class ISupplier

    Inherits WebService

 

    <WebMethodAttribute(), _

     SoapDocumentMethodAttribute( _

     "http://LearnXmlWS.com/Supplier/PlaceOrder", _

     RequestNamespace:="http://LearnXmlWS.com/Supplier", _

     ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

     Use:=Description.SoapBindingUse.Literal, _

     ParameterStyle:=SoapParameterStyle.Wrapped)> _

    Public MustOverride Function PlaceOrder( _

        ByVal newOrder As Order) As String

 

    <WebMethodAttribute(), _

     SoapDocumentMethodAttribute( _

       "http://LearnXmlWS.com/Supplier/CheckStatus", _

        RequestNamespace:="http://LearnXmlWS.com/Supplier", _

        ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

        Use:=Description.SoapBindingUse.Literal, _

        ParameterStyle:=SoapParameterStyle.Wrapped)> _

    Public MustOverride Function CheckStatus(ByVal OrderId As String) As _

        <XmlElementAttribute(IsNullable:=False)> OrderInfo

 

    <WebMethodAttribute(), _

     SoapDocumentMethodAttribute( _

        "http://LearnXmlWS.com/Supplier/GetPriceQuote", _

        RequestNamespace:="http://LearnXmlWS.com/Supplier", _

        ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

        Use:=Description.SoapBindingUse.Literal, _

        ParameterStyle:=SoapParameterStyle.Wrapped)> _

    Public MustOverride Function GetPriceQuote( _

        ByVal newOrder As Order) As QuoteInfo

End Class

 

Note that the class name is by default the same as the binding name, i.e. ISupplier. You’ll also see a WebServiceBinding attribute applied to ISupplier to set the binding’s name and namespace. Each method has a SoapDocumentMethod attribute that specifies things like the request and response namespaces and the fact that message parts are literal and are wrapped.

Notice also that the class is abstract (MustInherit). While you can easily put implementation code in the class itself, it is generally a good idea to put implementation code in a class that inherits from it. This way you will not be confused as to which methods are part of the original interface you are implementing versus methods you added yourself. Also, keeping the interface methods in a separate class means there’s less chance that you’ll accidently modify one or more of the interface methods as you are implementing the Web service.

Listing 8-4 shows an example Web service that implements the ISupplier interface by inheriting from the ISupplier abstract class that wsdl.exe generated.

Listing 8‑4 An example Web service that implements the ISupplier interface. (VBWSBook\Chapter8\InterfaceImpl\SingleInterfaceImple.asmx.vb)

Imports System.Web.Services

Imports System.Web.Services.Protocols

 

<WebServiceBinding( _

    Name:="ISupplier", _

    [Namespace]:="http://LearnXmlWS.com/Supplier", _

     Location:="http://vbwsserver/vbwsbook/chapter8/SingleInterface.wsdl"), _

    WebService(Namespace:="somenamespace")> _

Public Class SingleInterfaceImpl

    Inherits ISupplier

 

    <WebMethodAttribute(), _

    SoapDocumentMethodAttribute( _

    "http://LearnXmlWS.com/Supplier/CheckStatus", _

    RequestNamespace:="http://LearnXmlWS.com/Supplier", _

    ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

    Use:=Description.SoapBindingUse.Literal, _

    ParameterStyle:=Protocols.SoapParameterStyle.Wrapped, _

    Binding:="ISupplier")> _

    Public Overrides Function CheckStatus( _

ByVal OrderId As String) As OrderInfo

 

    End Function

 

    <WebMethodAttribute(), _

    SoapDocumentMethodAttribute( _

    "http://LearnXmlWS.com/Supplier/GetPriceQuote", _

    RequestNamespace:="http://LearnXmlWS.com/Supplier", _

    ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

    Use:=Description.SoapBindingUse.Literal, _

    ParameterStyle:=Protocols.SoapParameterStyle.Wrapped, _

    Binding:="ISupplier")> _

    Public Overrides Function GetPriceQuote( _

ByVal newOrder As Order) As QuoteInfo

 

    End Function

 

    <WebMethodAttribute(), _

    SoapDocumentMethodAttribute( _

        "http://LearnXmlWS.com/Supplier/PlaceOrder", _

        RequestNamespace:="http://LearnXmlWS.com/Supplier", _

        ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

        Use:=Description.SoapBindingUse.Literal, _

        ParameterStyle:=Protocols.SoapParameterStyle.Wrapped, _

        Binding:="ISupplier")> _

        Public Overrides Function PlaceOrder( _

ByVal newOrder As Order) As String

 

    End Function

End Class

The code in listing 8-4 is part of a Web project called InterfaceImpl. In this project, you’ll find the CSupplier.vb file that was generated by wsd.exe. The Web service class in listing 8-4 inherits from ISupplier (which is defined in CSupplier.vb). To implement the Web service interface as defined by ISupplier, you do the following steps.

·         Add a WebServiceBinding attribute on your Web service class (the class name is SingleInterfaceImpl in listing 8-4). Set the WebServiceBinding’s Name property to ISupplier and set its Location property to the URL of the interface WSDL. This way you are specifying that the interface definition should be imported from that URL rather than duplicated in your Web service’s WSDL.

·         Override each of the ISupplier class methods: CheckStatus, GetQuote and PlaceOrder.

·         On Each of these methods, set the Binding property of the SoapDocumentMethod attribute to ISupplier. Here you’re specifying that each of these methods belongs to the ISupplier interface.

 

Once you perform the above steps and compile your Web service, the resulting WSDL will look like the one in listing 8-5.

 

Listing 8‑5 WSDL for a Web service that implements the ISupplier interface. This WSDL imports the ISupplier definitions from SingleInterface.wsdl.

<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:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:i0="http://LearnXmlWS.com/Supplier"

xmlns:tns="somenamespace"

targetNamespace="somenamespace"

xmlns="http://schemas.xmlsoap.org/wsdl/">

<import namespace="http://LearnXmlWS.com/Supplier"

  location="http://vbwsserver/vbwsbook/chapter8/SingleInterface.wsdl" />

 <types />

  <service name="SingleInterfaceImpl">

    <port name="ISupplier" binding="i0:ISupplier">

      <soap:address

       location=

"http://vbwsserver/vbwsbook/chapter8/InterfaceImpl/SingleInterfaceImpl.asmx" />

    </port>

  </service>

</definitions>

The WSDL in listing 8-5 is lacking most of what you’re used to seeing in a WSDL document. It does not contain message, port, portType nor binding definitions. Instead, it imports the SingleInterface.wsdl document which contains the ISupplier interface definition. By having implementations reference the interface in this way, you avoid duplicating the interface definition with all the associated maintenance headaches.

 

Implementing Multiple Interfaces

The next logical step would be to implement multiple interfaces on the same Web service. You do this by applying multiple WebServiceBinding attributes to the class implementing the Web service. On each Web method you then set the SoapDocumentService or SoapRpcService Binding property to the name of the binding that contains this method. Taking the SupplierIface1 class in listing 8-1, you might want to factor its methods out into two interfaces: One called IOrderMgmt and the other called IQuoteMgmt. Listing 8-6 shows the code that does this.

 

Listing 8‑6 Defining multiple bindings (interfaces). (VBWSBook\Chapter8\Supplier2.asmx.vb)

    <WebServiceBinding( _

    Name:="IOrderMgmt", _

    [Namespace]:="http://LearnXmlWS.com/Supplier"), _

    WebServiceBinding( _

    Name:="IQuoteMgmt", _

    [Namespace]:="http://LearnXmlWS.com/Supplier"), _

    WebService([Namespace]:="http://LearnXmlWS.com/Supplier")> _

    Public Class SupplierIface2

        Inherits System.Web.Services.WebService

        <WebMethod( _

        Description:="Places the order then returns the new order id"), _

        SoapDocumentMethod(Binding:="IOrderMgmt")> _

        Public Function PlaceOrder(ByVal newOrder As Order) As String

            'returns a new order id

        End Function

        <WebMethod(), _

        SoapDocumentMethod(Binding:="IOrderMgmt")> _

        Public Function CheckStatus(ByVal OrderId As String) As OrderInfo

            'returns an orderinfo structure

        End Function

 

        <WebMethod(), _

        SoapDocumentMethod(Binding:="IQuoteMgmt")> _

        Public Function GetPriceQuote(ByVal newOrder As Order) As QuoteInfo()

            'returns an orderinfo structure

        End Function

   End Class

Note that there are two bindings with different names but they are both in the same namespace. Also, GetPriceQuote is now part of the IQuoteMgmt binding. Listing 8-7 shows the bindings defined in the resulting WSDL document.

 

Listing 8‑7 The resulting WSDL document with two bindings. (VBWSBook\Chapter8\MultiInterface.wsdl)

<binding name="IQuoteMgmt" type="s0:IQuoteMgmt">

 <soap:binding

       transport="http://schemas.xmlsoap.org/soap/http"

        style="document" />

  <operation name="GetPriceQuote">

    <soap:operation

      soapAction="http://LearnXmlWS.com/Supplier/GetPriceQuote"

      style="document" />

    <input>

      <soap:body use="literal" />

    </input>

    <output>

      <soap:body use="literal" />

    </output>

  </operation>

 </binding>

 

<binding name="IOrderMgmt" type="s0:IOrderMgmt">

  <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

  <operation name="PlaceOrder">

    <soap:operation

       soapAction="http://LearnXmlWS.com/Supplier/PlaceOrder"

       style="document" />

    <input>

      <soap:body use="literal" />

    </input>

    <output>

      <soap:body use="literal" />

    </output>

  </operation>

  <operation name="CheckStatus">

    <soap:operation

      soapAction="http://LearnXmlWS.com/Supplier/CheckStatus"

      style="document" />

    <input>

      <soap:body use="literal" />

    </input>

    <output>

      <soap:body use="literal" />

    </output>

  </operation>

</binding>

To implement these interfaces you run wsdl.exe with the /server parameter just like you did for the single interface case. However the output this time is different: You get one file that contains two classes one for each binding. The first class you get, IQuoteMgmt, has one method called GetPriceQuote. The second class, IOrderMgmt, has two methods called PlaceOrder and CheckStatus. You can add then add this file to your Web service project and create classes that inherit from each of IQuoteMgmt and IOrderMgmt then start implementing each interface’s methods. For example, listing 8-8 shows a Web service that implements IQuoteMgmt.

Listing 8‑8 An example Web service that implements IQuoteMgmt (VBWSBook\Chapter8\InterfaceImpl\QuoteMgmtImpl.asmx.vb)

Imports System.Web.Services

Imports System.Web.Services.Protocols

 

<WebServiceBinding( _

 Name:="IQuoteMgmt", _

 Namespace:="http://LearnXmlWS.com/Supplier", _

 Location:="http://vbwsserver/vbwsbook/chapter8/MultiInterface.wsdl"), _

 WebService(Namespace:="http://tempuri.org/")> _

Public Class QuoteMgmtImpl

    Inherits MultiIface1.IQuoteMgmt

 

    <WebMethodAttribute(), _

    SoapDocumentMethodAttribute( _

    "http://LearnXmlWS.com/Supplier/GetPriceQuote", _

     RequestNamespace:="http://LearnXmlWS.com/Supplier", _

     ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

     Use:=Description.SoapBindingUse.Literal, _

     ParameterStyle:=SoapParameterStyle.Wrapped, _

     Binding:="IQuoteMgmt")> _

     Public Overrides Function GetPriceQuote( _

            ByVal newOrder As InterfaceImpl.MultiIface1.Order) _

                    As InterfaceImpl.MultiIface1.QuoteInfo()

 

    End Function

End Class

Since .NET supports single inheritance, a class cannot inherit from more than one base class. This means you cannot create a Web service that inherits from both classes generated by wsdl.exe (the classes IQuoteMgmt and IOrderMgmt). Which means you would end up with a Web service class for each binding you want to implement.

This is not exactly what comes to mind when I think multiple interfaces. Ideally, we can have one Web service class that implements both IQuoteMgmt and IOrderMgt. While it’s possible to achieve this, you can’t do it by inheriting from abstract classes generated by wsdl.exe. Instead, you must create a Web service class then manually specify the bindings and the methods. The process is almost identical to the single-interface implementation case except you are not overriding any base class methods. The steps for implementing multiple interfaces on a single Web service class are:

·         Create a Web service.

·         For each interface you want to implement: Add a WebServiceBinding attribute to the Web service class. Specify the binding’s name, namespace, and location (the URL of a WSDL document where the binding is defined).

·         Create WebMethods on this Web service that correspond to the operations defined in each binding. You can get some help from wsdl.exe by running it with the /server flag then copying the abstract method definitions it creates and pasting them into your Web service. If you do this, be sure to remove the MustOverride keyword from those methods.

·         For each Web method, specify the binding name. This name must match one of the binding names defined by SoapServiceBinding attributes on the Web service class.

 

Listing 8-9 shows an example Web service class that implements both IQuoteMgmt and IOrderMgmt.

 

Listing 8‑9 A single Web service class that implements both IQuoteMgmt and IOrderMgmt  interfaces (VBWSBook\Chapter8\InterfaceImpl\MultipleInterfaceImpl.asmx.vb).

Imports System.Web.Services

Imports System.Web.Services.Protocols

 

<WebService(Namespace:="http://tempuri.org/"), _

WebServiceBindingAttribute(Name:="IQuoteMgmt", _

 [Namespace]:="http://LearnXmlWS.com/Supplier", _

 Location:="http://vbwsserver/vbwsbook/chapter8/MultiInterface.wsdl"), _

 WebServiceBindingAttribute(Name:="IOrderMgmt", _

 [Namespace]:="http://LearnXmlWS.com/Supplier", _

Location:="http://vbwsserver/vbwsbook/chapter8/MultiInterface.wsdl") _

> _

Public Class MultipleInterfaceImpl

    Inherits WebService

 

    <WebMethodAttribute(), _

        SoapDocumentMethodAttribute( _

        "http://LearnXmlWS.com/Supplier/GetPriceQuote", _

        RequestNamespace:="http://LearnXmlWS.com/Supplier", _

        ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

        Use:=Description.SoapBindingUse.Literal, _

        ParameterStyle:=SoapParameterStyle.Wrapped, _

        Binding:="IQuoteMgmt")> _

    Public Function GetPriceQuote( _

                ByVal newOrder As InterfaceImpl.MultiIface1.Order) _

                As InterfaceImpl.MultiIface1.QuoteInfo()

    End Function

 

    <WebMethodAttribute(), _

     SoapDocumentMethodAttribute( _

     "http://LearnXmlWS.com/Supplier/PlaceOrder", _

     RequestNamespace:="http://LearnXmlWS.com/Supplier", _

     ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

     Use:=Description.SoapBindingUse.Literal, _

     ParameterStyle:=SoapParameterStyle.Wrapped, _

     Binding:="IOrderMgmt")> _

    Public Function PlaceOrder( _

            ByVal newOrder As InterfaceImpl.MultiIface1.Order) As String

 

    End Function

 

 

    <WebMethodAttribute(), _

     SoapDocumentMethodAttribute( _

     "http://LearnXmlWS.com/Supplier/CheckStatus", _

     RequestNamespace:="http://LearnXmlWS.com/Supplier", _

     ResponseNamespace:="http://LearnXmlWS.com/Supplier", _

     Use:=Description.SoapBindingUse.Literal, _

     ParameterStyle:=SoapParameterStyle.Wrapped, _

     Binding:="IOrderMgmt")> _

    Public Function CheckStatus(ByVal OrderId As String) _

                            As InterfaceImpl.MultiIface1.OrderInfo

 

    End Function

End Class

The Web service class in listing 8-9 has two WebServiceBinding attributes for IQuoteMgmt and ISupplierMgmt. Inside the class, you’ll see the GetPriceQuote Web method which belongs to the IQuoteMgmt interface. You’ll also see the IOrderMgmt interface methods: PlaceOrder and CheckStatus. Listing 8-10 shows the resulting WSDL.

Listing 8‑10 The WSDL for the service in listing 8-9.

<?xml version="1.0" encoding="utf-8"?>

<definitions 

xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"

xmlns:s="http://www.w3.org/2001/XMLSchema"

xmlns:i0="http://LearnXmlWS.com/Supplier"

xmlns:tns="http://tempuri.org/"

targetNamespace="http://tempuri.org/"

xmlns="http://schemas.xmlsoap.org/wsdl/">

  <import namespace="http://LearnXmlWS.com/Supplier"

  location="http://vbwsserver/vbwsbook/chapter8/MultiInterface.wsdl" />

  <types />

  <service name="MultipleInterfaceImpl">

    <port name="IOrderMgmt" binding="i0:IOrderMgmt">

      <soap:address

location=

"http://vbwsserver/vbwsbook/chapter8/InterfaceImpl/MultipleInterfaceImpl.asmx" />

    </port>

    <port name="IQuoteMgmt" binding="i0:IQuoteMgmt">

      <soap:address

location=

"http://vbwsserver/vbwsbook/chapter8/InterfaceImpl/MultipleInterfaceImpl.asmx" />

    </port>

  </service>

</definitions>

The most interesting feature of the WSDL in listing 8-10 is the presence of two <port> elements inside the <service> element indicating that the service implements two different interfaces.

 

Interfaces in Different Namespaces

Here’s an interesting twist on the above scenario: What if we placed the bindings in different namespaces? For example IQuoteMgmt in a namespace called http://LearnXmlWS.com/QuoteMgmt and IOrderMgmt in another namespace called http://LearnXmlWS.com/OrderMgmt. Listing 8-11 shows the code for SupplierIface3 which has two bindings each in its own namespace.

Listing 8‑11 A Web service with two bindings in two different namespaces. (VBWSBook\Chapter8\Supplier3.asmx.vb).

<WebServiceBinding( _

Name:="IOrderMgmt", _

[Namespace]:="http://LearnXmlWS.com/OrderMgmt"), _

WebServiceBinding( _

Name:="IQuoteMgmt", _

[Namespace]:="http://LearnXmlWS.com/QuoteMgmt"), _

WebService([Namespace]:="http://LearnXmlWS.com/Supplier")> _

Public Class SupplierIface3

        Inherits System.Web.Services.WebService

        <WebMethod( _

  Description:="Places the order then returns the new order id"), _

        SoapDocumentMethod(Binding:="IOrderMgmt")> _

        Public Function PlaceOrder(ByVal newOrder As Order) As String

            'returns a new order id

        End Function

        <WebMethod(), _

        SoapDocumentMethod(Binding:="IOrderMgmt")> _

        Public Function CheckStatus(ByVal OrderId As String) As OrderInfo

            'returns an orderinfo structure

        End Function