WCF – Merge WSDL in a single file

It can happen that old clients dislike the way WCF separate WSDL descriptors. By default, a WCF service contains the full WSDL in an external “link” specified by the wsdl:import directive. If I browse the following service
http://localhost:8695/GiftMessageService.svc?wsdl
i’ll get a description of the service (like ports, address) but nothing about the soap actions and messages we’ll need to use. There is the wsdl:import for that.

<wsdl:import namespace="http://tempuri.org/" location="http://localhost:8695/GiftMessageService.svc?wsdl=wsdl0" />

So, if we navigate to “http://localhost:8695/GiftMessageService.svc?wsdl=wsdl0″ we’ll get an extensive description of the operations supported, the types and so on. What WCF doesn’t allow to do is to merge these two wsdl in one file (url).  Luckly there are a few tools out there helping us, I’ve tried WCFExtras: it does a loads of feature not provided natively with WCF and one of these features is the chanche of combining the WSDL in one file only.

Download the component, add it as a reference in your project and follow the documentation, the relevant section we will need in the config is the following:

<endpointBehaviors>
  <behavior name="Sample.WsdlSampleEndpointBehavior">
    <wsdlExtensions singleFile="true"/>
  </behavior>
</endpointBehaviors>

Let’s give a try by browsing the service. We’ll probably get this error:

An ExceptionDetail, likely created by IncludeExceptionDetailInFaults=true, whose value is: System.InvalidOperationException: An exception was thrown in a call to a WSDL export extension: WCFExtras.Wsdl.WsdlExtensions Endpoint: http://localhost:8695/GiftMessageService.svc —-> System.ApplicationException: Single file option is not supported in multiple wsdl files.

In order to fix it both the class and the contract MUST have the same namespace:

[ServiceContract(Namespace= "http://Sample/GiftMessageService")]
public interface IGiftMessageService
{
  [OperationContract]
  string GetEncodedGiftMessageImage(int deliveryNumber, int lineItem);

  [OperationContract]
  string GetEncodedGiftMessageImageWithQuality(int deliveryNumber, int lineItem, int quality);
}
[ServiceBehavior(Namespace = "http://Sample/GiftMessageService")]
public class GiftMessageService : IGiftMessageService
{....

Two easy ways to expose a WCF as a WebService (even asmx)

Assuming we’re all using Visual Studio 2010, start by creating two projects: a WCF Service library project and a WCF

image image

The service library will contain the contracts and their interfaces with all the service attributes while the service application will be the project exposed by our web server, it will have the *.svc and *.asmx files along with the config file containing all the WCF settings. Let’s see the simplest contract possible:

[ServiceContract]
public interface ITimeManager
{
   [OperationContract]
   DateTime GetActualTime();
}

public class TimeManager : ITimeManager
{
   public DateTime GetActualTime()
   {
       return DateTime.UtcNow;
   }
}

We create two services in the service application containing the reference to the contact, nothing prevent us from creating:

image

Where the markup will be the following:

//This will be included in TimeServiceSVC.svc
<%@ ServiceHost Language="C#" Debug="true" Service="Giorgio.WCFasWS.Library.TimeManager" %>

//This will be included in TimeServiceASMX.asmx
<%@ WebService  Language="C#" Debug="true" Class="Giorgio.WCFasWS.Library.TimeManager, Giorgio.WCFasWS.Library "%>

If we now try to browse one of the services on IIS we will probably get the following exception:  Could not load type ‘System.ServiceModel.Activation.HttpModule’ from assembly ‘System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089′

Register Asp.Net 4.0 to fix the error by running aspnet_regiis -iru (go here for more details)

If you want your WS to be fully exposed (and declare it to use basic profile) decorate the contract as follows:

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1, EmitConformanceClaims = true)]
[ServiceContract]
public interface ITimeManager
{
    [WebMethod]
    [OperationContract]
    DateTime GetActualTime();
}

public class TimeManager : ITimeManager
{
    public DateTime GetActualTime()
    {
        return DateTime.UtcNow;
    }
}