AJ's blog

January 24, 2015

WCF Policy – Part 7: Implementing the Default Behavior

Filed under: .NET, .NET Framework, C#, SOA, Software Architecture, Software Development, WCF — ajdotnet @ 3:43 pm

Our policy demands that we read and manipulate the messages going in and out, on client and service. The WCF component designated for this demand is a message inspector, which is available in two flavors, designated by the interface: IClientMessageInspector for the client, and IDispatchMessageInspector for the service. Both interfaces provide two methods; one is called for the incoming message, the other for the outgoing message. The respective runtimes – ClientRuntime und DispatchRuntime – each maintain a collection of respective message inspector instances.

On the client the contract behavior created by our import extension can add the message inspector in IContractBehavior.ApplyClientBehavior, nothing more than a one-liner:

void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
    clientRuntime.MessageInspectors.Add(new TrackingClientMessageInspector(endpoint));
}

On the server, our service behavior can do the same, only it needs to address all eligible endpoints:

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
    foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
    {
        foreach (EndpointDispatcher ep in cd.Endpoints)
        {
            var inspector = new TrackingDispatchMessageInspector(ep);
            ep.DispatchRuntime.MessageInspectors.Add(inspector);
        }
    }
}

If the application developer calls a service method on the client proxy class, the WCF creates a message object for the resulting SOAP message. It the proceeds to call IClientMessageInspector.BeforeSendRequest on all message inspectors.

In this method our TrackingClientMessageInspector creates an ITrackingInformation object to maintain the information, creates the first tracking entry for sending the message, passes it to your header class, which will later serialize it as SOAP header, and adds it to the collection of headers. Finally it returns the ITrackingInformation object as correlation state:

private ITrackingInformation GetTrackingInformation()
{
    var tracking = new TrackingInformation();
    return tracking;
}

object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
{
    // get headers
    var tracking = GetTrackingInformation();
    
    // add send information
    tracking.OutgoingHeaders.TrackingEntries.Add(new TrackingEntry(_thisClient, "Send Request to " + _calledService));
    
    // tracking entries header
    var trackingEntriesHeader = new TrackingEntriesHeader(tracking.OutgoingHeaders.TrackingEntries);
    request.Headers.Add(trackingEntriesHeader);
    
    // pass headers via correlation state
    return tracking;
}

Now the SOAP message makes its journey to the service, where the WCF will create a respective message object and call IDispatchMessageInspector.AfterReceiveRequest on all message inspectors there.

Our TrackingDispatchMessageInspector deserializes the SOAP header, puts the information in a ITrackingInformation object, adds its tracking entry for receiving the message, and, again, returns the ITrackingInformation object as correlation state.

private ITrackingInformation GetTrackingInformation()
{
    var extension = new TrackingInformation();
    return extension;
}

object IDispatchMessageInspector.AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
    // install operation extension
    var tracking = GetTrackingInformation();
    
    // tracking entries header
    var trackingEntriesHeader = TrackingEntriesHeader.ReadHeader(request.Headers);
    if (trackingEntriesHeader != null)
        tracking.IncomingHeaders.TrackingEntries.AddRange(trackingEntriesHeader.TrackingEntries);
    
    // add receive information
    tracking.IncomingHeaders.TrackingEntries.Add(new TrackingEntry(_thisService, "Received Request" ));
    
    // and pass on to outgoing headers
    tracking.OutgoingHeaders.TrackingEntries.AddRange(tracking.IncomingHeaders.TrackingEntries);
    
    // pass headers via correlation state
    return tracking;
}

The WCF proceeds to call the service method, the application developer provided. Afterwards the same process goes backwards for the reply. The WCF calls IDispatchMessageInspector.BeforeSendReply for all message inspectors. It also passes in the correlation state we returned earlier. Without this information we wouldn’t be able to link the two calls in case several requests came in in parallel.

Our message inspector adds the respective tracking entry, puts in in our header class, and is done:

void IDispatchMessageInspector.BeforeSendReply(ref Message reply, object correlationState)
{
    var tracking = (ITrackingInformation)correlationState;
    
    // add send information
    tracking.OutgoingHeaders.TrackingEntries.Add(new TrackingEntry(_thisService, "Send Reply"));
    
    // tracking entries header
    var trackingEntriesHeader = new TrackingEntriesHeader(tracking.OutgoingHeaders.TrackingEntries);
    reply.Headers.Add(trackingEntriesHeader);
}

The SOAP message is sent back to the client. There the WCF again creates a message object and calls IClientMessageInspector.AfterReceiveReply for all message inspectors, passing in the correlation state, similar to what we saw on the server.

Our message inspector deserializes the SOAP header, adds its own tracking entry, and we’re done:

void IClientMessageInspector.AfterReceiveReply(ref Message reply, object correlationState)
{
    var tracking = (ITrackingInformation)correlationState;
    
    // tracking entries header
    var trackingEntriesHeader = TrackingEntriesHeader.ReadHeader(reply.Headers);
    if (trackingEntriesHeader != null)
        tracking.IncomingHeaders.TrackingEntries.AddRange(trackingEntriesHeader.TrackingEntries);
    
    // add receive information
    tracking.IncomingHeaders.TrackingEntries.Add(new TrackingEntry(_thisClient, "Received Reply from " + _calledService));
}

That’s it.

Let recap that in context: The application developer has put a service behavior on his service. This has lead to a respective contract behavior on the client. And with what we just provided, the two of them are working together at runtime to create a protocol of calls at runtime.

The final topic we have to address is making the information available to the developer.

 

That’s all for now folks,
AJ.NET

Advertisement

1 Comment »

  1. Reblogged this on Dinesh Ram Kali..

    Comment by dineshramitc — February 9, 2015 @ 5:48 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: