8.2 Implementing an Interface
Whether you define the interfaces yourself or you work with interfaces defined by someone else, you'll eventually want to implement them. Although you can 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 this. 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/Single- Interface.wsdl /l:VB /out:CSupplier.vb
Listing 8.3 shows the interesting 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, that is, 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 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 about which methods are part of the original interface you are implementing and which ones you added yourself. Also, keeping the interface methods in a separate class means there's less chance that you'll accidentally 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 wsdl.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
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 specify 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.
Set the Binding property of the SoapDocumentMethod attribute to ISupplier on each of these methods. Here you're specifying that each of these methods belongs to the ISupplier interface.
Once you perform these 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, or binding definitions. Instead, it imports the SingleInterface.wsdl document that 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.