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
Reblogged this on Dinesh Ram Kali..
Comment by dineshramitc — February 9, 2015 @ 5:48 am