Create a Single WSDL File for WCF Services in .NET 4.5

One of the things that I’ve always found to be annoying is that WSDL files generated for WCF services that reference objects in different namespaces include wsdl:import links to other WSDLs. This separation by namespace may make sense from an organizational/maintainability standpoint, but it makes it difficult to provide a WSDL for a privately hosted service to a third party consumer doing external development.

I was excited to learn that .NET 4.5 has an option to generate WSDLs as a single file–what a great idea! It’s easy to do, too. All you do is append ?singlewsdl to your service instead of the standard ?wsdl. When you do this, all of the types from the various namespaces that might be in-use are included in a single file. It’s wonderful!

Read more about this and other new enhancements to WCF in .NET 4.5 here.

WCF Client Closal & Disposal

Say you’re consuming a WCF service. You’ve added a service reference, and you’ve got a client proxy. It implements IDisposable. So, when it’s time to use it, you should just chuck it in a using statement and not worry about additional resource cleanup, right?

Not so fast. There can be problems with the using statement. The problem is prevalent enough to warrant an article on MSDN demonstrating the improper use of using to automatically clean up resources when using a typed client.

So what should you be using instead of using? One option, which is outlined in the aforementioned MSDN article, is to use a try/catch block. If an exception occurs while closing the client, the client’s Abort method should be called. MSDN’s guidance suggests reporting errors and calling Abort for expected exceptions and calling Abort then re-throwing unexpected exceptions. The article explains, “Typically there is no useful way to handle unexpected errors, so typically you should not catch them when calling a WCF client communication method.”

// MSDN recommendation for WCF client cleanup
try
{
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

There’s a lot of quick & dirty advice online that suggests using a try/finally block to first Close the client if it’s not in a faulted state, then Abort it if it’s not in a closed state. This is probably fine for small, simple applications, but hiding all exceptions can lead to maintenance woes later by reducing the visibility of underlying problems.

// effective but hides errors
try
{
    if (client.State != CommunicationState.Faulted)
    {
        client.Close();
    }
}
finally
{
    if (client.State != CommunicationState.Closed)
    {
        client.Abort();
    }
}

I like the suggestion laid out in this blog article. The author suggests re-implementing IDisposable in a partial class. The problem I have with this approach is that it may be confusing to other developers. When the client is used in the application, it will be wrapped in a using. There’s no good way for a developer to know whether that class has been properly re-implemented without doing further investigation. I think it’s easier for developers to have a hard rule: don’t use using with WCF clients.

So, instead of using a partial class, I’d suggest creating a wrapper that implements IDisposable. Developers using the client don’t need to worry about closing connections or error-handling; they can just throw a using around it an move on.

Here’s what that might look like. You could implement the service contract interface to ensure all methods are exposed, or have a generic method that accepts a lambda expression.

// An IDisposable client wrapper
public class Client : IDisposable
{
    private WcfClient _client;
    
    public Client()
    {
        _client = new WcfClient();
    }
    
    public void SomeMethod()
    {
        _client.SomeMethod();
    }
    
    void IDisposable.Dispose()
    {
        Dispose(true);
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_client == null)
            {
                return;
            }
            
            try
            {
                if (_client.State != CommunicationState.Faulted)
                {
                    _client.Close();
                }
            }
            catch (CommunicationException e)
            {
                //...
                _client.Abort();
            }
            catch (TimeoutException e)
            {
                //...
                _client.Abort();
            }
            catch (Exception e)
            {
                //...
                _client.Abort();
                throw;
            }
            finally
            {
                if (_client.State != CommunicationState.Closed)
                {
                    _client.Abort();
                }
                _client = null;
            }
        }
    }
 
    ~Client()
    {
        Dispose(false);
    }
}

A Simple WCF Service Callback Example

I’ve done a lot with WCF services over the past few years, but I haven’t done much with callbacks. I wanted to write a simple application to understand and demonstrate how a callback service works in WCF. The example described below was implemented in a single solution with two console application projects, one client and one server.

Create the server

The first thing we’ll do is create and host our WCF service. This will be done in five short steps:

  1. Create the service contract
  2. Create the service callback contract
  3. Create the service implementation
  4. Modify the endpoint configuration
  5. Host the service

To create the service contract, service implementation, and default endpoint configuration, you can use Visual Studio’s menu to choose Add New Item > Visual C# Items > WCF Service. I named my service MyService, and so my service contract is called IMyService. Visual Studio creates the service contract with a single method, DoWork. I decided that I wanted my service to have two methods: OpenSession and InvokeCallbackOpenSession will be used by the server to store a copy of the callback instance, and InvokeCallback will be used to trigger a callback call to the client from the client.

Here’s my complete IMyService contract:

using System.ServiceModel;

namespace CallbackService.Server
{
    [ServiceContract(CallbackContract = typeof(IMyServiceCallback))]
    public interface IMyService
    {
        [OperationContract]
        void OpenSession();
    }
}

Note that the service contract indicates the callback contract in its attribute. The callback contract, IMyServiceCallback, will have a single method, OnCallback. Here is the complete IMyServiceCallback interface:

using System.ServiceModel;

namespace CallbackService.Server
{
    public interface IMyServiceCallback
    {
        [OperationContract]
        void OnCallback();
    }
}

The third step requires us to implement our service. When OpenSession is called, I create a timer that will invoke the callback once per second. Note the ServiceBehavior attribute that sets the ConcurrencyMode to Reentrant. If you do not change the ConcurrencyMode to Multiple or Reentrant, the channel will be locked and the callback will not be able to be invoked. Here is the complete MyService implementation:

using System;
using System.ServiceModel;
using System.Timers;

namespace CallbackService.Server
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
    public class MyService : IMyService
    {
        public static IMyServiceCallback Callback;
        public static Timer Timer;

        public void OpenSession()
        {
            Console.WriteLine("> Session opened at {0}", DateTime.Now);
            Callback = OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();

            Timer = new Timer(1000);
            Timer.Elapsed += OnTimerElapsed;
            Timer.Enabled = true;
        }

        void OnTimerElapsed(object sender, ElapsedEventArgs e)
        {
            Callback.OnCallback();
        }
    }
}

When we added the new WCF service to the project, Visual Studio added the default endpoint configuration to the app.config. By default, wsHttpBinding is used as the binding. We want to change it to use wsDualHttpBinding which supports two-way communication. I also changed the URL to be friendlier, but that is not necessary. Here’s my final app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="CallbackService.Server.MyService">
        <endpoint address="" binding="wsDualHttpBinding" contract="CallbackService.Server.IMyService">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8090/CallbackService.Server/MyService/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

The final step is to simply host the service. Since my server is a console application, I do this in the Program.Main method. Here is the complete class:

using System;
using System.ServiceModel;

namespace CallbackService.Server
{
    class Program
    {
        static void Main(string[] args)
        {
            var host = new ServiceHost(typeof(MyService));
            host.Open();
            Console.WriteLine("Service started at {0}", DateTime.Now);
            Console.WriteLine("Press key to stop the service.");
            Console.ReadLine();
            host.Close();
        }
    }
}

Create the client

With the server complete, creating the client is a breeze. We’ll complete the client in just three steps:

  1. Add a service reference
  2. Implement the callback class
  3. Create the client proxy

To add the service reference, you must have the service running. I did this by simply running the server executable outside of Visual Studio. You can copy the URL from the server’s app.config. Right-click References in your client project, choose Add Service Reference, and paste the URL. I named my service reference MyServiceReference.

The service reference pulls in IMyServiceCallback, which will allow us to create our callback class. To do this, just add a new, empty class to the project. I named my class MyServiceCallback, and when the callback is invoked, it just writes a line to the console. Here is the complete implementation:

using System;
using CallbackService.Client.MyServiceReference;

namespace CallbackService.Client
{
    public class MyServiceCallback : IMyServiceCallback
    {
        public void OnCallback()
        {
            Console.WriteLine("> Received callback at {0}", DateTime.Now);
        }
    }
}

The client application is also a console application, so creating and using the proxy client will occur in its Program.Main. I added a pause to allow the server time to host the service, and then I invoke the remote procedure OpenSession. This will trigger the service to begin executing callbacks to the client application every second, resulting in a line written to the console. Here’s is the contents of my Program.cs:

using System;
using System.ServiceModel;

namespace CallbackService.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press enter to continue once service is hosted.");
            Console.ReadLine();

            var callback = new MyServiceCallback();
            var instanceContext = new InstanceContext(callback);
            var client = new MyServiceReference.MyServiceClient(instanceContext);
            client.OpenSession();

            Console.ReadLine();
        }
    }
}

Conclusion

And that’s all there is to it! This is clearly a simple, unorganized example with few complexities, but it demonstrates the core functionality provided by callbacks in WCF services. This capability allows for push updates and other real-time communication between client and server, and it really opens a world of possibilities. This is certainly a welcome option in my ever-growing development toolbox!