2

Is it possible/how do I stop and start the HTTP MEX listener of a self hosted WCF service at runtime without affecting the primary WCF service?

(Please don't ask why I want to do this. It is a hack to get around artificial limitations imposed by someone else.)

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
chilltemp
  • 8,854
  • 8
  • 41
  • 46

1 Answers1

4

*****[Re-added this answer after re-test and code cleanup] This is actual code that I have added to my generic WCF-based service development framework and it is fully tested.*****

Assuming that you start with MEX enabled on the ServiceHost...

The following solution is written in terms of a ServiceHost subclass (WCFServiceHost<T>) that implements a special interface (IWCFState) for storing an instance of the MEX EndpointDispatcher class.

First, add these namespaces...

using System.ServiceModel;
using System.ServiceModel.Dispatcher;

Secondly, define the IWCFState interface...

public interface IWCFState
{
    EndpointDispatcher MexEndpointDispatcher
    {
        get;
        set;
    }
}

Thirdly, create a static class for some ServiceHost extension methods (we'll fill them in below)...

public static class WCFExtensions
{
    public static void RemoveMexEndpointDispatcher(this ServiceHost host){}

    public static void AddMexEndpointDispatcher(this ServiceHost host){}
}

Now let's fill in the extension methods...

Stopping MEX on a ServiceHost at Runtime

public static void RemoveMexEndpointDispatcher(this ServiceHost host)
{
    // In the simple example, we only define one MEX endpoint for
    // one transport protocol
    var queryMexChannelDisps = 
            host.ChannelDispatchers.Where(
                disp => (((ChannelDispatcher)disp).Endpoints[0].ContractName
                                            == "IMetadataExchange"));
    var channelDisp = (ChannelDispatcher)queryMexChannelDisps.First();

    // Save the MEX EndpointDispatcher
    ((IWCFState)host).MexEndpointDispatcher = channelDisp.Endpoints[0];

    channelDisp.Endpoints.Remove(channelDisp.Endpoints[0]);
}

Then call it like this...

// WCFServiceHost<T> inherits from ServiceHost and T is the Service Type,
// with the new() condition for the generic type T.  It encapsulates 
// the creation of the Service Type that is passed into the base class 
// constructor.
Uri baseAddress = new Uri("someValidURI");
WCFServiceHost<T> serviceImplementation = new WCFServiceHost<T>(baseAddress);

// We must open the ServiceHost first...
serviceImplementation.Open();

// Let's turn MEX off by default.
serviceImplementation.RemoveMexEndpointDispatcher();

Starting MEX (again) on a ServiceHost at Runtime

public static void AddMexEndpointDispatcher(this ServiceHost host)
{
    var queryMexChannelDisps =
            host.ChannelDispatchers.Where(
                    disp => (((ChannelDispatcher)disp).Endpoints.Count == 0));
    var channelDisp = (ChannelDispatcher)queryMexChannelDisps.First();

    // Add the MEX EndpointDispatcher
    channelDisp.Endpoints.Add(((IWCFState)host).MexEndpointDispatcher);
}

Then call it like this...

serviceImplementation.AddMexEndpointDispatcher();

Summary

This design allows you to use some messaging methods to send a command to the service itself or to code that is hosting the service and have it carry out the enabling or disabling of a MEX EndpointDispatcher, effectively turning off MEX for that ServiceHost.

Note: This design assumes that the code will support MEX at startup, but then it will use a config setting to determine if the service will disable MEX after calling Open() on the ServiceHost. This code will throw if you attempt to call either extension method before the ServiceHost has been opened.

Considerations: I would probably create a special service instance with management operations that did not support MEX at startup and establish that as service control channel.

Resources

I found the following two resources indispensable while figuring this out:

  • I am not completely satisfied with my solution: 1) I don't like assuming only 1 EndpointDispatcher and specifying ContractName instead of comparing a type; 2) I don't like assuming that the MEX ChannelDispatcher has 0 endpoints and that it is the only such ChannelDispatcher. – EnocNRoll - AnandaGopal Pardue Feb 16 '09 at 15:31
  • I also do not like the extension methods themselves, because of the need to embed the cast to IWCFSTate. Therefore, I might just move them into my WCFServiceHostUtils static class instead. – EnocNRoll - AnandaGopal Pardue Feb 16 '09 at 15:50
  • 1
    Thanks, this was extremely helpful. I modified my version a bit to support multiple Mex endpoints, and it works as desired. – chilltemp Feb 16 '09 at 23:52
  • I am glad! If you have any links to resources that might have helped you any further, like on a way to support multiple Mex endpoints, I would love to see that. Cheers! – EnocNRoll - AnandaGopal Pardue Feb 17 '09 at 00:22
  • 1
    I'm pretty sure that my '2 Mex endpoints' are nothing more than an artifact of my learning WCF. There is actually only 1 Mex address exposed. – chilltemp Feb 17 '09 at 17:13