Configuration for a WCF Client in a DLL

By default, WCF Web Service clients have their binding and endpoint configurations defined by their hosting EXE’s app.config–or web.config if your host is an IIS application. The DLL itself has no control over the config file: the app.config that is created in Visual Studio when you create a Web Service Reference is misleading–the system.serviceModel XML node in that file has to be copied over to the host’s app.config or web.config. This isn’t convenient when you have multiple environments or if you are creating a DLL to hide the web service details behind a client fascade. There are three ways I know of to get around this limitation:

  1. Programmaticaly set up the bindings and endpoints (and parameterize the variation points).
  2. Use the ConfigurationChannelFactory (good but requires thoughtful design).
  3. Create a new AppDomain (excessive).

I’m going to show you the first and simplest way: programmaticaly setting up the bindings and endpoints. My opinion is that handling different configuration/system.serviceModel XML for web service clients is overkill and overcomplicates deployment. I tend to use XML configuration for WCF web services and code configuration for web clients.

Later I’ll give links to the other techniques, but seeing the simplest solution first may be heplful. The tradeoff is that the simplest solution is also the least flexible.

Before I show you my code snippets, an explanation of the classes may help:

  • public class FooClient:
     Fascade to simplfy client calls to the Foo Web Service.
     Contains an instance of FooXmit.
  • internal class FooXmit:
     Communication (transmit) details, including endpoing and bindings.
     Contains instance of FooProxy.
  • namespace FooProxy:
    Service Reference: WCF client generated from the Foo service WSDL.
    One or more proxy classes generated from the Foo Service WSDL; the FooDataRequest class is once such class.
public class Client
{
    private readonly FooXmit _xmit;

    // Note that our connection variation points are parameters
    public Client(Uri serviceUrl, string user, string password, double timeoutSeconds)
    {
        _xmit = new FooXmit(serviceUrl, user, password, timeoutSeconds);
    }
}

public class FooXmit
{
    private readonly FooProxy.FooDataRequest _proxy;
    public FooXmit(Uri serviceUrl, string user, string password, double timeoutSeconds)
    {
        var binding = new BasicHttpBinding();
        binding.SendTimeout = TimeSpan.FromSeconds(timeoutSeconds);

            // This contrived example isn't that secure (use what makes sense in your environment)
        binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
        binding.Security.Transport.Realm = "FOO";

        binding.MessageEncoding = WSMessageEncoding.Text;

        var remoteAddress = new EndpointAddress(serviceUrl);

        _proxy = new SDLCMChangeManagementClient(binding, remoteAddress);
        if(_proxy.ClientCredentials == null)
        {
            throw new NullReferenceException("_proxy.ClientCredentials was null!"); // inconceivable!
        }
         _proxy.ClientCredentials.UserName.UserName = user;
         _proxy.ClientCredentials.UserName.Password = password;  
    }

    public FooResponse SendRequest(FooDataRequest data)
    {
        var response = new FooResponse();
        try
        {
            SafeOpen(_proxy);
            response = SendRequest(data, response);
        }
        catch (TimeoutException ex)
        {
            response.RetCode = FooResponse.RetCodeType.Failure;
            response.ErrMessage = String.Format("Communication with FOO Service *timed out*: {0}", ex.Message);
        }
        catch (CommunicationException ex)
        {
            response.RetCode = FooResponse.RetCodeType.Failure;
            response.ErrMessage = String.Format("Communication fault with FOO Service: {0}", ex.Message);
        }
        catch (Exception ex)
        {
         vresponse.RetCode = FooResponse.RetCodeType.Failure;
             response.ErrMessage = String.Format("Unexpected error: {0}", ex.Message);
        }
        finally
        {
             SafeClose(_proxy);
        }
        return response;
    }

    private static void SafeOpen(ICommunicationObject proxy)
    {
        if (proxy.State == CommunicationState.Opened)
        {
            return;
        }
        if (proxy.State == CommunicationState.Faulted)
        {
            proxy.Abort();
        }
        proxy.Open();
    }

    private static void SafeClose(ICommunicationObject proxy)
    {
        if (proxy.State == CommunicationState.Faulted)
        {
            proxy.Abort();
        }
        else
        {
            proxy.Close();
        }
    }

    // The actual method is here (overloaded)
    private FooResponse SendRequest(FooDataRequest data, FooResponse response)
    {
    ...
    }
...
}

In the above example, we would store our connection variation points: Uri serviceUrl, string user, string password, double timeoutSeconds …in some sort of configuration file. You could also define an interface/class as a parameter instead (inject interface in constructor, serialize/deserialize class for the configuration data).

For an explanation on the try/catch and SafeOpen/SafeClose, see “Don’t Wrap WCF Hosts or Clients in a Using Statement.”

Here two other ways to get endpoint/binding configuration to work for a WCF Client in a DLL: