DCSIMG
Obtaining an Untyped WCF Message from a Typed Service Operation - All Your Base Are Belong To Us

All Your Base Are Belong To Us

Mostly .NET internals and other kinds of gory details

Obtaining an Untyped WCF Message from a Typed Service Operation

Most WCF services operate on a typed message contract.  In other words, the underlying Message object is not available because it is parsed by the default WCF operation invoker into the data contract that the service operation expects.

However, oftentimes you need access to the underlying Message object even though your typed service does not directly consume it.  For example, you might want to automatically serialize the message, or pass it to other services that expose an untyped message contract (such as routing services or notification publishing services outlined in my previous posts).

This can be accomplished by making a copy of the incoming message and later obtaining it from within the typed service operation.  The most intuitive interception point for making such a copy is an implementation of IDispatchMessageInspector.  The most intuitive location for storing the message is a message property, because it is transient and does not get serialized into subsequent message calls.  (Note that storing the message in thread-local storage is an appealing option, but the service call is not guaranteed to be performed on the same thread that the message inspector is called on.  In fact, from experience, this is rarely the case with one-way operations.)

Consequently, we need is a message property for caching the message, which can install itself onto the current operation context's incoming message properties collection, and retrieve itself from that collection:

public sealed class MessageCacheProperty

{

    public const string Name = "MessageCacheProperty";

 

    public Message Message { get; private set; }

 

    public MessageCacheProperty(Message message)

    {

        Message = message;

    }

 

    public static Message GetContextMessage()

    {

        OperationContext ctx = OperationContext.Current;

        MessageCacheProperty messageProperty =

            (MessageCacheProperty)

            ctx.IncomingMessageProperties[Name];

        return messageProperty.Message;

    }

    public static void Install(Message message)

    {

        OperationContext ctx = OperationContext.Current;

        ctx.IncomingMessageProperties.Add(

            Name, new MessageCacheProperty(message));

    }

}

This property can now be installed using an implementation of IDispatchMessageInspector that is installed on our service's endpoints (through an IEndpointBehavior or an IServiceBehavior extension):

public sealed class MessageCacheInspectorAttribute :

    IDispatchMessageInspector

{

    public object AfterReceiveRequest(

        ref Message request,

        IClientChannel channel,

        InstanceContext instanceContext)

    {

        MessageBuffer copy =

            request.CreateBufferedCopy(int.MaxValue);

        MessageCacheProperty.Install(copy.CreateMessage());

        request = copy.CreateMessage();

 

        return null;

    }

 

    //The operation is one-way,

    //so this won't be called anyway

    public void BeforeSendReply(

        ref Message reply,

        object correlationState)

    {

    }

}

Note that we make two copies of the messages, because copying the message consumes it and makes it unusable for subsequent processing.

The above code makes the message available in any service operation that passes through this inspector, e.g.:

Message toSend = MessageCacheProperty.GetContextMessage();

Comments

Eyal Vardi said:

הי,

אם הבנתי אותך נכון אז אני חושב שאתה יכול לכתוב את הדבר הבא ולקבל את אותו תוצאה

OperationContext.Current.RequestContext.RequestMessage

כל עוד יש

OperationContext

זה יחזיר

את ההודעה.

# June 16, 2008 12:06 AM

I Love C# said:

בעקבות הפוסט דרך קצרה יותר: OperationContext.Current.RequestContext.RequestMessage למעשה אפשר להגיעה

# June 18, 2008 12:31 AM

Sasha Goldshtein said:

Translating Eyal's comment:

"If I understand you correctly, I think you can write the following and obtain the same result:

OperationContext.Current.RequestContext.RequestMessage

As long as there is an OperationContext it will return the message."

My response:

In a one-way service call, there is an OperationContext but there is no RequestContext.  Therefore, you can't always rely on the RequestContext to obtain the message.

# June 22, 2008 8:54 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: