Adding Custom WCF header to Endpoint Programatically for Reliable Sessions

John K picture John K · Jun 30, 2010 · Viewed 10.9k times · Source

I'm building a WCF router and my client uses Reliable Sessions. In this scenario when the client opens a channel a message is sent (establishing a Reliable Session?). Its contents is as follows:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
    <a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
    <a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
  </s:Header>
  <s:Body>
    <CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
      <AcksTo>
        <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </AcksTo>
      <Offer>
        <Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
        <Endpoint>
          <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
        </Endpoint>
        <IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
      </Offer>
    </CreateSequence>
  </s:Body>
</s:Envelope>

The problem here is that the headers do not contain any information I can use to look up what service to route the message to. In Busatmante's router sample code she gets around this by adding a header to the endpoint:

  <client>
    <endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
      bindingConfiguration="wsHttp"
      contract="localhost.IMessageManagerService" >
      <headers>
        <Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
      </headers>
    </endpoint>
  </client>

When the reliable session is opened the message contains this custom header.

<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>

This is great; however, I have a requirement to configure the client programatically. I figured that the ChannelFactory Endpoint would have a Header object to which I could manually add my custom header. Unfortunately it does not. So I did some searching and found some recomendations to extend WCF by implementing an IClientMessageInspector to add my header and adding it as a behavior to my endpoint.

public class ContractNameMessageInspector : IClientMessageInspector {

    private const string HEADER_NAME = "ContractName";
    private readonly string _ContractName;

    public ContractNameMessageInspector(string contractName) {
        _ContractName = contractName;
    }

    #region IClientMessageInspector Members

    public void AfterReceiveReply(ref Message reply, object correlationState) { }

    public object BeforeSendRequest(ref Message request, IClientChannel channel) {

        HttpRequestMessageProperty httpRequestMessage;
        object httpRequestMessageObject;

        if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
            httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
            if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
                httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
            }
        }
        else {
            httpRequestMessage = new HttpRequestMessageProperty();
            httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
            request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
        }
        return null;
    }

    #endregion
}

So when my client makes a service call the message contains the custom header but the message establishing the Reliable Sessions still does not.

So my question is; how do I add a custom header to the Endpoint programatically in such a way that the reliable session message contains it?

Many Thanks

Answer

John K picture John K · Jul 3, 2010

Figured it out. Although there is no property or method to add a header to an EndpointAddress there is an optional parameter on the constructor.

_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());

_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);

Now when I receive the message establishing the reliable session it actually does contain my custom header. This kinda makes sense as the message inspector most likely only operates on dispatched messages while the message establishing the reliable session is generated by lower level WCF code.