views:

316

answers:

1

Hello,

I am using the WCF IClientMessageInspector to send information in a header to a WCF service (wsHTTP). I am using the IDispatchMessageInspector to receive the information and populate a String property.

I verified the header is sending the information properly as I use the FindHeader within my specific method but I'd rather just access the custom class that has the Token property and GET the Token from there rather than having to do FindHeader in a separate method that all other methods call to get the header value.

So my question is, from the server side (OperationContext I presume) how do I access this class instance that has the Token property populated with the header info?

Here is the code from the entire class below:

Region " IMPORTS "

Imports System.ServiceModel
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Description
Imports System.ServiceModel.Channels
Imports System.ServiceModel.Configuration

End Region

Public Class MessageInspector
    Inherits BehaviorExtensionElement
    Implements IClientMessageInspector, IDispatchMessageInspector, IEndpointBehavior

    Private Const headerName As String = "HeaderToken"
    Private Const headerNamespace As String = "urn:com.nc-software.services:v1"

    Private _token As String
    Public Property Token() As String
        Get
            Return _token
        End Get
        Set(ByVal Value As String)
            _token = Value
        End Set
    End Property

    Public Overrides ReadOnly Property BehaviorType() As System.Type
        Get
            Return GetType(MessageInspector)
        End Get
    End Property

    Protected Overrides Function CreateBehavior() As Object
        Return New MessageInspector
    End Function

Region " IEndpointBehavior "

Public Sub AddBindingParameters(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal bindingParameters As System.ServiceModel.Channels.BindingParameterCollection) Implements System.ServiceModel.Description.IEndpointBehavior.AddBindingParameters
End Sub

Public Sub ApplyClientBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal clientRuntime As System.ServiceModel.Dispatcher.ClientRuntime) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyClientBehavior
    clientRuntime.MessageInspectors.Add(Me)
End Sub

Public Sub ApplyDispatchBehavior(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint, ByVal endpointDispatcher As System.ServiceModel.Dispatcher.EndpointDispatcher) Implements System.ServiceModel.Description.IEndpointBehavior.ApplyDispatchBehavior
    endpointDispatcher.DispatchRuntime.MessageInspectors.Add(Me)
End Sub

Public Sub Validate(ByVal endpoint As System.ServiceModel.Description.ServiceEndpoint) Implements System.ServiceModel.Description.IEndpointBehavior.Validate
End Sub

End Region

Region " IClientMessageInspector "

Public Sub AfterReceiveReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IClientMessageInspector.AfterReceiveReply
End Sub

Public Function BeforeSendRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel) As Object Implements System.ServiceModel.Dispatcher.IClientMessageInspector.BeforeSendRequest
    Dim header As New MessageHeader(Of String)(Token)
    Dim untypedHeader As MessageHeader = header.GetUntypedHeader(headerName, headerNamespace)
    request.Headers.Add(untypedHeader)
    Return Nothing
End Function

End Region

Region " IDispatchMessageInspector "

Public Function AfterReceiveRequest(ByRef request As System.ServiceModel.Channels.Message, ByVal channel As System.ServiceModel.IClientChannel, ByVal instanceContext As System.ServiceModel.InstanceContext) As Object Implements System.ServiceModel.Dispatcher.IDispatchMessageInspector.AfterReceiveRequest
    Try
        Dim headers As MessageHeaders = OperationContext.Current.IncomingMessageHeaders
        Dim headerIndex As Integer = headers.FindHeader(headerName, headerNamespace)
        If headerIndex >= 0 Then
            Token = headers.GetHeader(Of String)(headerIndex)
        End If
    Catch
    End Try
    Return Nothing
End Function

Public Sub BeforeSendReply(ByRef reply As System.ServiceModel.Channels.Message, ByVal correlationState As Object) Implements System.ServiceModel.Dispatcher.IDispatchMessageInspector.BeforeSendReply
End Sub

End Region

End Class

A: 

Based on patterns I see the WCF team themsevles establishing, my suggestion would be to have your IDispatchMessageInspector shove the value of the header into the current OperationContext's IncomingMessageProperties dictionary. By doing this, the value will be tied to the current operation context and carried through all stages of execution properly for you by the WCF runtime.

As far as how to read that value further down the stack out you can do two things. First, you can expose the string key you will be using to read/write the value to the properties collection on a static readonly string somewhere that other code can use it to retrieve the value from the OperationContext.Current themselves like so:

int value = (int)OperationContext.Current.IncomingMessageProperties[MyMessageProperty.MyHeader];

Now, this still requires a lot of coding on the part of all the people who need to read the value. Getting the current context, indexing into the dictionary with the key and casting the result to the proper type (I used int as a sample above). If you want to get fancy, the next step you can take is to instead just expose these properties via your own context class so people can just access them like normal, strongly typed CLR properties. That might look a little something like this:

First, implement a static accessor property on a class called MyOperationContext:

public static int MyHeader
{
    get
    {
        return (int)OperationContext.Current.IncomingMessageProperties[MyMessageProperty.MyMessageProperty];
    }

    set
    {
        OperationContext.Current.IncomingMessageProperties[MyMessageProperty.MyMessageProperty] = value;
    }
}

Now in your various implementations that need to read this header they would simply do:

int value = MyOperationContext.MyHeader;
Drew Marsh