Yesterday we looked at how we might
add a little context so we can tie seemingly disparate events together again.
Today we'll look at passing this context across a WCF service boundary by authoring a behavior extension. If tracing isn't your thing then this post should also interest anybody who would want to send a header with every request to a WCF service.
Here, we'll check for an ActivityId (that isn't the default GUID value {000...000}) and sneak it into all messages leaving our client in a Soap Header. Then we'll have quick look for this soap header on the server and change the server threads ActivityId if we find one.
If you wanted to fiddle with the SOAP crossing the wire in the .asmx world you'd implement a
SoapExtension cover your self in SOAP and play with bubbles. In WCF, however, things are a little different and, in my opinion, better.
Fiddling with messages
In WCF, if you want to fiddle with messages closer to the WCF stack you have to create an implementation of either
IClientMessageInspector or
IDispatchMessageInspector for the client and server respectively.
Remember, these roles can easily be reversed in WCF thanks to
Duplex Contracts and the like.
Here's how we'll add our soap header to outgoing messages from the client
private const string HEADER_NAME = "suggestedActivityId";
private const string HEADER_NS = "urn:thejoyofcode-com:services:activity-header:2006-11";
private static Guid DEFAULT_GUID = new Guid();
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
// don't send this if it's {000...000}
if (System.Diagnostics.Trace.CorrelationManager.ActivityId != DEFAULT_GUID)
{
// before we send the request, we need to add our suggested context header
MessageHeader<Guid> mh = new MessageHeader<Guid>();
mh.Content = System.Diagnostics.Trace.CorrelationManager.ActivityId;
mh.Actor = string.Empty;
mh.MustUnderstand = false;
// add the new header to our request
request.Headers.Add(mh.GetUntypedHeader(HEADER_NAME, HEADER_NS));
}
return null;
}
and here's how we'll grab them on the server
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
// Now we need to mine out our suggested activityId
int index = request.Headers.FindHeader(HEADER_NAME, HEADER_NS);
if (index > -1)
{
Guid activityId = request.Headers.GetHeader<Guid>(HEADER_NAME, HEADER_NS);
if (activityId != DEFAULT_GUID)
{
System.Diagnostics.Trace.CorrelationManager.ActivityId = activityId;
}
}
return null;
}
That was easy! Sadly, we're not quite there yet. To plug the inspectors into WCF we need to implement
IEndpointBehavior and
BehaviorExtensionElement. The latter of which is necessary to support configuration via your Xml config file.
Fortunately, this part is pretty easy so rather than bore you with it here
download the code and check it out for yourself.
Configuring a behavior extension is pretty easy
<behaviors>
<serviceBehaviors>
<behavior name="messageServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="withActivityInspectors">
<activityInspectors />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="activityInspectors" type="TheJoyOfCode.ServiceHeaderSample.ActivityHeaderBehavior, TheJoyOfCode.ServiceHeaderSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
Now we just have to specify a "withActivityInspectors" behaviourConfiguration attribute on the applicable endpoints (server and client).
And here's what our soap header looks like over the wire (using a BasicHttpBinding anyway):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<suggestedActivityId xmlns="urn:thejoyofcode-com:services:activity-header:2006-11">6f9f9fd1-09ab-4fdd-ba30-ea7d7759a77f</suggestedActivityId>
The
source code also contains a little application to show the inspectors in action. The application acts as both server
and client so it's perhaps not the most compelling example for a distributed technology but it does at least pass the ActivityId with the service call.
Note that I implemented both message inspector interfaces in the same class, which is private and belongs to the class that implements IEndpointBehavior.
UPDATE: fixed the bad link to the source code. Apologies.

Post By
Josh Twist
4:52 AM
29 Nov 2006
» Next Post:
Prevalence
« Previous Post:
Brighten up your traces with a splash of colour
Comments are closed for this post.
Posted by
Josh
@
07 Mar 2007
4:58 AM
Minor point but I just realised that I could have used the static Guid.Empty property instead of creating my own empty guid (DEFAULT_GUID).