views:

2802

answers:

2

By defining an attribute that implements IContactBehavior and IWsdlExportExtension and set that attribute on your service contract, you can easily add Soap Headers to your wsdl (see http://wcfextras.codeplex.com/ for more information)

But now I need to set a Soap Header contract in the wsdl on all Operationcontracts and this time I cannot set an attribute.

Following code (called from IWsdlExportExtension.ExportEndPoint) doesn't work, but does work when called from the SoapHeaderAttributes (that executes an IWsdlExportExtension.ExportContract)

foreach (OperationDescription operationDescription in context.ContractConversionContext.Contract.Operations)
{
   AddSoapHeader(operationDescription, "SomeHeaderObject", typeof(SomeHeaderObject), SoapHeaderDirection.InOut);                    
}

internal static void AddSoapHeader(OperationDescription operationDescription, string name, Type type, SoapHeaderDirection direction)
{
 MessageHeaderDescription header = GetMessageHeader(name, type);
 bool input = ((direction & SoapHeaderDirection.In) == SoapHeaderDirection.In);
 bool output = ((direction & SoapHeaderDirection.Out) == SoapHeaderDirection.Out);

 foreach (MessageDescription msgDescription in operationDescription.Messages)
 {
  if ((msgDescription.Direction == MessageDirection.Input && input) ||
   (msgDescription.Direction == MessageDirection.Output && output))
   msgDescription.Headers.Add(header);
 }
}

internal static MessageHeaderDescription GetMessageHeader(string name, Type type)
{
 string headerNamespace = SoapHeaderHelper.GetNamespace(type);
 MessageHeaderDescription messageHeaderDescription = new MessageHeaderDescription(name, headerNamespace);
 messageHeaderDescription.Type = type;
 return messageHeaderDescription;
}

Anyone has an idea how to apply this code on all operations (without using attributes) and by doing this, adding the contract of the header to the wsdl ?

+1  A: 

You might want to have a look at the WCFExtras project on CodePlex - it has some support for custom SOAP headers and stuff like that. Not 100% sure if it's capable of filling your need, but check it out!

Marc

UPDATE: have you looked into creating a WCF extension, e.g. something like a message inspector, on both the client and the server side?

The client side IClientMessageInspector defines two methods BeforeSendRequest and AfterReceiveReply while the server side IDispatchMessageInspector has the opposite methods, i.e. AfterReceiveRequest and BeforeSendReply.

With this, you could add headers to every message going across the wire (or selectively only to a few).

Here's a snippet from a IClientMessageInspector implementor we use to automagically transmit the locale information (language and culture info) across from the clients to the server - should give you an idea how to get started:

public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
    International intlHeader = new International();
    intlHeader.Locale = CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;

    MessageHeader header = MessageHeader.CreateHeader(WSI18N.ElementNames.International, WSI18N.NamespaceURI, intlHeader);
    request.Headers.Add(header);

    return null;
}
marc_s
I was actually pointing out to WCFExtras myself, but I couldn't find out how to add a soap header to the wsdl on all operations without setting attributes.
Linefeed
<hehe> I totally missed your link to wcf extras.....
marc_s
I don't see how this applies on modifying the WSDL Contract. Can you clarify ?
Linefeed
I doesn't. Inspectors are nice, but what they do is not reflected in the WSDL contract, so the client has to be aware of it...
Philippe
+1  A: 

The IEndpointBehavior has the following interface:

ApplyDispatchBehavior(ServiceEndpoint endPoint, EndPointDispatcher endpointDispatcher);

You can add Soap Headers to the wsdl for operations by iterating over the endpoint.Contract.Operations in the ApplyDispatchBehavior.

Here you have the complete solution that worked for me:

void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
    foreach (OperationDescription operationDescription in endpoint.Contract.Operations)
    {
        foreach (MessageDescription msgDescription in operationDescription.Messages)
        {
            AddSoapHeader(operationDescription, "SomeHeaderObject", typeof(SomeHeaderObject), SoapHeaderDirection.InOut);
        }
    }
}

internal static void AddSoapHeader(OperationDescription operationDescription, string name, Type type, SoapHeaderDirection direction)
{
    MessageHeaderDescription header = GetMessageHeader(name, type);
    bool input = ((direction & SoapHeaderDirection.In) == SoapHeaderDirection.In);
    bool output = ((direction & SoapHeaderDirection.Out) == SoapHeaderDirection.Out);

    foreach (MessageDescription msgDescription in operationDescription.Messages)
    {
            if ((msgDescription.Direction == MessageDirection.Input && input) ||
                    (msgDescription.Direction == MessageDirection.Output && output))
                    msgDescription.Headers.Add(header);
    }
}

internal static MessageHeaderDescription GetMessageHeader(string name, Type type)
{
    string headerNamespace = SoapHeaderHelper.GetNamespace(type);
    MessageHeaderDescription messageHeaderDescription = new MessageHeaderDescription(name, headerNamespace);
    messageHeaderDescription.Type = type;
    return messageHeaderDescription;
}

The SoapHeaderHelper can be found in the WcfExtras.

Linefeed