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