Monday, March 29, 2010

Hooking Your class into WCF

In this article I am talking about how you can intercept the SOAP Calling and handling of non existences message of service contract. In this example Add is a dummy method which is not a part of service contract I just mimic the web service request and response handling.

You can create you class which will be inherited from IServiceBehavior and then use ApplyDispatchBehavior of this interface to set you hook as message inspector.

void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
//ApplyDispatchBehavior

foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher endpoint in channelDispatcher.Endpoints)
{
//endpoint.DispatchRuntime.OperationSelector = this;
endpoint.DispatchRuntime.MessageInspectors.Add(new PrintMessageInterceptor());
}
}
}
}


In this method PrintMessageInterceptor is an instance of another class which is inherited from IDispatchMessageInspector which provide us two method AfterReceiveRequest and BeforeSendReply

In AfterReceiveRequest you can take out body of SOAP and then you can set you parameter and in BeforeSendReply you can check this parameter and modified the response message.

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
IClientChannel channel, InstanceContext instanceContext)
{

XmlNodeReader reader;
XmlDocument document = new XmlDocument();
document.Load(request.GetReaderAtBodyContents());
//Get the body element operation
string bodyElement = document.DocumentElement.LocalName;
switch(bodyElement)
{
case "Add" :
strAction = Action.Add;
break;
}
return null;
}


public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply,
object correlationState)
{
MessageBuffer buffer = reply.CreateBufferedCopy(Int32.MaxValue);
reply = buffer.CreateMessage();
//Get the body element operation
XmlDocument document;
string bodyElement;

document = new XmlDocument();
document.Load(reply.GetReaderAtBodyContents());
//Get the body element operation
bodyElement = document.DocumentElement.LocalName;
switch (strAction.ToString())
{
case "Add":
Add(document);
reply = Message.CreateMessage(reply.Version, null, localReader);
break;
}
}

Now in this case Add method will take the body of the SOAP and modified that one and send the same body to the calling method.localReader is of XmlNodeReader which I have set to create the response message.

private void Add(XmlDocument passedValue)
{

if (passedValue.DocumentElement.Name.Equals("Add"))
{
using (var passedReader = XmlReader.Create(new System.IO.MemoryStream(System.Text.Encoding.ASCII.GetBytes(passedValue.InnerXml))))
//using (var writer = XmlWriter.Create(new System.IO.MemoryStream(System.Text.Encoding.ASCII.GetBytes(reader.ReadInnerXml())), settings))
{
while (passedReader.Read())
{
switch (passedReader.NodeType)
{
case XmlNodeType.Element:
break;

case XmlNodeType.Text:
Sum += Convert.ToInt32(passedReader.Value);
break;
}
}
}
newNode = passedValue.CreateElement("AddResponse", "http://tempuri.org/");
childNode = passedValue.CreateElement("AddResult", "http://tempuri.org/");
childNode.InnerText = Sum.ToString();
newNode.AppendChild(childNode);
passedValue.RemoveChild(passedValue.DocumentElement);
passedValue.InnerXml = (newNode.OuterXml);

}

Let me know your feedback.

No comments: