WSDL-First development with WCF
A couple weeks ago I mentioned that you could do WSDL-First development with WCF, but I didn't go into detail as to how that would work. Somebody asked, so I guess I'll describe the specific steps. I want to use a real scenario, so for a WSDL, I will use the WSDL that Microsoft defines for Microsoft Office Research Services.
Did you get that? Microsoft Office defines a WSDL for Research Services. Any Microsoft Office program, Office 2003 or Office 2007, can call out to any service that implements the given Research Service wire contract. Office programs are web services clients. Ok, we're all clear on that, right?
And this it may be counter intuitive for some people. The client application, in this case, Microsoft Office, specifies the on-the-wire contract, the WSDL. Lots of people have a server-centric design perspective, and assume that the server defines the contract. That often makes sense, but not in this case. Because there are so many deployments of Microsoft Office out there, it makes sense for the client to define the contract.
Back to the WSDL. But let me be straight with you: on the website I referenced, I did not actually see a WSDL specifically defined for the Office Research Service. I'd expect to find it in the Schema Reference, but that reference includes XML Schema, not web services contracts. On the other hand, the Research Service SDK includes sample applications built on .NET and ASMX, and running those you can generate the WSDL that is being used. So... effectively the WSDL is implicitly published. I took some liberties with the WSDL available in that way and I came up with my own WSDL, like so:
<definitions
xmlns:http="https://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="https://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="https://www.w3.org/2001/XMLSchema"
xmlns:s0="urn:Microsoft.Search"
xmlns:soapenc="https://schemas.xmlsoap.org/soap/encoding/"
xmlns:wsaw="https://www.w3.org/2006/05/addressing/wsdl"
targetNamespace="urn:Microsoft.Search"
xmlns="https://schemas.xmlsoap.org/wsdl/"
>
<types>
<s:schema elementFormDefault="qualified" targetNamespace="urn:Microsoft.Search">
<s:element name="Registration">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="registrationXml" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="RegistrationResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="RegistrationResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Query">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="queryXml" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="QueryResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="QueryResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="Status">
<s:complexType />
</s:element>
<s:element name="StatusResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="StatusResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</types>
<message name="RegistrationSoapIn">
<part name="parameters" element="s0:Registration" />
</message>
<message name="RegistrationSoapOut">
<part name="parameters" element="s0:RegistrationResponse" />
</message>
<message name="QuerySoapIn">
<part name="parameters" element="s0:Query" />
</message>
<message name="QuerySoapOut">
<part name="parameters" element="s0:QueryResponse" />
</message>
<message name="StatusSoapIn">
<part name="parameters" element="s0:Status" />
</message>
<message name="StatusSoapOut">
<part name="parameters" element="s0:StatusResponse" />
</message>
<portType name="IResearchServiceSoap">
<operation name="Registration">
<input message="s0:RegistrationSoapIn" wsaw:Action="urn:Microsoft.Search/Registration" />
<output message="s0:RegistrationSoapOut" wsaw:Action="" />
</operation>
<operation name="Query">
<input message="s0:QuerySoapIn" wsaw:Action="urn:Microsoft.Search/Query" />
<output message="s0:QuerySoapOut" wsaw:Action="" />
</operation>
<operation name="Status">
<input message="s0:StatusSoapIn" wsaw:Action="urn:Microsoft.Search/Status" />
<output message="s0:StatusSoapOut" wsaw:Action="" />
</operation>
</operation>
</portType>
<binding name="ResearchServiceSoap" type="s0:IResearchServiceSoap">
<soap:binding transport="https://schemas.xmlsoap.org/soap/http" style="document" />
<operation name="Registration">
<soap:operation soapAction="urn:Microsoft.Search/Registration" style="document" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="Query">
<soap:operation soapAction="urn:Microsoft.Search/Query" style="document" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
<operation name="Status">
<soap:operation soapAction="urn:Microsoft.Search/Status" style="document" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
</definitions>
Ok, starting with that WSDL, given to us by Microsoft Office, we now want to build a service that implements that WSDL, and we want to build it in WCF. We start with the svcutil.exe tool, which is shipped with the .NET SDK v3.0 , or later.
Continuing on with the thought from above: most people have a server-centric design perspective when it comes to web services; most people think the server comes first, then you build the clients...The svcutil.exe tool is also guilty of that. In fact there is documentation that describes how to build a client using the svcutil.exe tool, but as far as I know there is no doc that describes how to create a WCF service from a WSDL file using the svcutil.exe tool. We're going to fix that. Despite the fact that there is no doc for this, it is in fact a supported scenario. Constructing the service starting from the WSDL works.
Here's how you generate a server-side "stub" and the interface for that WSDL:
c:\netsdk3.0\bin\svcutil.exe /language:C# /out:IResearchService.cs /n:*,Ionic.Samples.Webservices.Sep24 ResearchService.wsdl
On the command line, I specify the output file, the language, and also the WSDL file itself. If there are external XSD files, you will need to specify those, too. In this case, the entire contract is contained within a single WSDL file. The last interesting bit is the /n switch, which I use to specify the namespace for all the generated classes. Without this switch the interface and the Data Access Objects are all generated into the default (global::) namespace, which I don't like.
You can see that I reference the .NET SDK v3.0 directory for the svcutil.exe tool. WCF first shipped in the .NET SDK v3.0. Even if you have .NET SDK 3.5 installed, (or Visual Studio 2008), you will still use the svcutil.exe from .NET 3.0. This is because of the russian-doll model of releases that .NET 2.0, 3.0 and 3.5 are.
Now, this is a command line, which is definitely not a GUI. But you can teach Visual Studio to do this for you. To do this, you need to go to the Tools menu, and select External Tools. Then click Add, and specify these settings for the svcutil tool:
Of course you have to specify the relevant path for the .NET SDK V3.0, for your installation. Regardless whether you run svcutil.exe from an MSbuild file, or from Visual Studio, here's what the generated interface looks like:
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="urn:Microsoft.Search",
ConfigurationName="Ionic.Samples.Webservices.Sep24.IResearchServiceSoap")]
public interface IResearchServiceSoap
{
[System.ServiceModel.OperationContractAttribute(Action="urn:Microsoft.Search/Registration",
ReplyAction="")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
string Registration(string registrationXml);
[System.ServiceModel.OperationContractAttribute(Action="urn:Microsoft.Search/Query",
ReplyAction="")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
string Query(string queryXml);
[System.ServiceModel.OperationContractAttribute(Action="urn:Microsoft.Search/Status",
ReplyAction="")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
string Status();
}
The Office Research Service interface is a bit of a bummer, because the operations on this service interface just exchange strings. No complex types here. But trust me, it works the same if you use complex types in the interface. In that case, the output source file will include a .NET DataContract definition for the various complex types, along with XML namespace settings and so on. The silly thing is that the Office Research Service does actually send back XML. But rather than use xsd:anyType to allow any XML, or even a strictly-specified XML element, the office app just specifies a string. In effect, the XML is encoded as a string. When implementing the service, you would actually instantiate an XmlDocument and load in the string you get passed, if you know what I mean. This is normally something the web services runtime would do for you, if you specify your WSDL that way. But Office did not do that.
Ok, moving on.... Now, If you are sharp-eyed, you will notice that the command line to generate the server-side stub and interface is the same command line you would use for the Client proxy. And you're right. In fact if you look in the generated file you will find some client-specific proxy classes. No problem with any of that. For the client we need those proxy classes, but for the server we don't. In a server-specific project, that generated code will remain unused, or of course you could manually remove it from the generated file.
At this point, you have the interface. Now you need the implementation. In Visual Studio, if you begin to type in a class definition, and then type in a colon and specify the name of an interface, you can right click on that interface and ask Visual Studio to generate method stubs for all the interface methods. Perfect. As well, you will need to decorate your WCF Service class with the ServiceBehavior attribute. It looks like this:
[ServiceBehavior(Name="WcfResearchLibrary",
Namespace="urn:Microsoft.Search",
IncludeExceptionDetailInFaults=true)]
After that, you fill in the implementation for those methods. Then you need to consider the service host for your app. If it will run in IIS, then you code up a .svc file; if you want to host it in a console app, then you use some of the boilerplate I mentioned in a previous post. And so on.
Then you need to deal with the configuration settings, another issue I dealt with in the previous post. And that's pretty much it.
You have now coded a WCF Service using WSDL-First design principles.
Comments
Anonymous
September 24, 2008
PingBack from http://www.easycoded.com/wsdl-first-development-with-wcf/Anonymous
October 22, 2008
Thanks for the great article, however I don't agree with you that using wsdl files means client comes first. when you design your wsdl file this mean this is the server contract and client should abide to itAnonymous
October 23, 2008
I certainly did not want to imply that using a WSDL file means you must build the client first. However, I disagree that the WSDL file is a "server contract." In fact it is a contract, an interface definition, and as a first-class artifact in the system design, it is impossible to assign ownership of it purely to server-side assets or client-side assets. The WSDL defines the link between them, and so is part of both, or is equally independent of both. The Office Research Schema provides the counter-evidence to your statement that "using a WSDL file means this is the server contract and the client should abide to it."Anonymous
December 30, 2008
Hi, good article but I agree with Tareq. I would guess you are quite alone with your "WSDL is not a server contract" and "it is impossible to assign ownership of it" statements.
- The service interface (WSDL) must have an owner. It is owned by the service provider and used by the service consumer (terms server and client are often used). Shared ownership is not possible as the service provider must define the services it provides and the consumers/clients must respect that.
- You are confusing or misusing the term "client". The fact that a workstation running Office is able to act as a service provider doesn't (or shouldn't) lead you to the conclusion that the WSDL can be defined by the "client". The clients of the web service are the parties who call the web service. They can run server OS and have 32 processors, but they are still clients of the web service and have no say on the WSDL that is published by the service provider (i.e. server).
Anonymous
January 09, 2009
@Jarkko - thanks for the thoughts. You have stated that the WSDL must have an owner, and then you assert that it is owned by the service provider. But you are simply asserting - it is not clear that you are correct. The counter-example I provide - MS Office providing the service interface - is nowhere refuted or discounted in your comment? Also, I am quite clear on the meaning of Client and Server. From your comment, it seems that you are not clear on the role that the MS Office application plays, while running on a desktop. It is not the service provider. It is the service consumer. It consumes a service as defined by the WSDL published by the Office Research SDK. Office is a consumer. Now, it is also true that the MS Office team hosts a research service online that conforms to that WSDL. But that is one specific instance of the general WSDL. The instance does not define the WSDL. The instance conforms to the WSDL.Anonymous
February 28, 2009
I agree with the original poster - the WSDL doesn't "belong" to either. To prove the point, I hope to use this method (if I can get it working!) to implement an an International Standard for medical document exchange, which is itself defined by WSDL files, which BOTH clients and servers need to abide by. This WSDL was written to convey the required information independently of any particular implementations, so that it works equally well with Microsoft, java etc.Anonymous
March 06, 2009
Dave Harvey, that is a service-oriented philosophy!Anonymous
March 17, 2009
I also agree with the original poster. The WSDL simply defines an agreement between two endpoints stating what messages they will be sending between each other. The primary application could be a consumer of a web service, where you may want to allow the consumer to speak to any number of different web services using the same agreed methods and data definitions, or indeed the web service where you may want to allow many consumers to call it.Anonymous
May 29, 2009
I wanted to point something out regarding the WSDL-First item I posted yesterday . This is a look atAnonymous
August 04, 2009
I am indebted to you for posting a sample of the generated interface, because when I ran svcutil.exe against the WSDL I was provided, the OperationContractAttribute came out a little differently: [System.ServiceModel.OperationContractAttribute(Action="http://www.hotel.info/HDE_ReservationPushService/V1_0/PushReservation", ReplyAction="*")] Notice the darn asterisk there? I didn't, at first, and whenever I built my service, the operation was not available (the service-generated WSDL didn't even mention it). Then I saw that your example did not have an asterisk, and I manually modified the svcutil-generated code to use a blank instead of an asterisk, and the service-generated WSDL was finally full to the brim with the operation and all the data structures necessary. I found this article: http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/41f5fe72-3ab3-4741-867e-a93119fe62aa in which someone else encounters the same problem. ...So, is it really a problem for svcutil.exe to create server-side-usable interfaces? What happened to my good friend "wsdl.exe /serverInterface"?Anonymous
September 07, 2009
Thank you also for this good example. I have been writing a proof of concept of WSRP 2.0 using this exact method, and so far it has worked. The Tip by Alexander Oss, was also invaluable. I too found that the svcutil generated the ReplyAction="*" which did stop it from generating the WSDL. Removing this from the entry resolved the problem. Once I have the working example of WSRP 2.0 with a .NET service I will post a blog about it. Cheers Ramon Buckland ( ramon at thebuckland.com )Anonymous
January 31, 2011
its very usefull for meAnonymous
March 10, 2011
Thanks for great article. for some one just starting on this technology its great help. One quick question on sharing data between server side and client side, we've a new project for some document exchange, which is itself defined by WSDL file, which BOTH clients and servers need to abide by. as per specs each session authenticated by a session key. since there is likely multiple users using service where/how do we store this session info? Better if there is basic sample that shows actually data sharing between server and client. many thanks.Anonymous
January 07, 2014
The OP is correct - the WSDL is not necissarily owned by the server. It can be owned by the server, client, or shared by both. Consider an application that manages other applications user permissions via a web service. Each managed application (server) has its own web service that is consumed by the manager in order to manage the user permissions. So there are many servers, but one client. To plug my application into the manager application and allow it to manage my permissions, I have to expose a web service that conforms to their service contract. Clearly in this case the service contract (WSDL) is owned by the client, the manager application.Anonymous
March 17, 2015
Check this out as well: msdn.microsoft.com/.../ms735109%28v=vs.110%29.aspx