HOWTO: Implement async handling in an IMessageSink
Question
I am using remoting with http/soap. I have a custom client formatting sink provider which creates its own custom client formatting sink during the configuration.
I placed my custom sink provider in the configuration file just before soap formatter provider so that it would be created before soap formatter provider. When I test it, every thing works fine as it was intended and I can break into my custom client formatter sink during debugging.
Here is the code for SyncProcessMessage for my custom formatter sink.
public IMessage SyncProcessMessage(IMessage msg)
{
IMessage retMsg = ((IMessageSink) _nextSink).SyncProcessMessage(msg);
return retMsg;
}I simply delegate the work to next sink in the sink chain.
Here is my question:
I want to make an asynchronous call on the next sink in the chain instead of sync call. The method to be used is SoapClientFormatterSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) which returns IMessageCtrl which only method is called Cancel.
Since this is async call, the call will return immediately. How do I retrieve the reply message? Obviously I have to wait until I get the reply message. But where do I need to look for the message?
BTW: SoapClientFormatterSink.AsyncProcessMessage returns null.
Answer
You have to create another class which implement IMessage sink and which will take the response from the call as soon as it's available:
public class SomeReplySink: IMessageSink
{
IMessageSink _next;
public SomeReplySink(IMessageSink next)
{
_next = next;
}
public IMessageSink NextSink
{
get { return _next; }
}
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
// nothing to do, response will be synchronous (i.e. the handling of the
// asynchronous response will itself be handled synchronously)
}
public IMessage SyncProcessMessage(IMessage msg)
{
// [HERE YOU CAN CHANGE THE REPLY WHENEVER IT ARRIVES!]
System.Console.WriteLine("REPLY:SyncProcessMessage()");
// pass it on to the next reply sink
return _next.SyncProcessMessage(msg);
}
}Then you can do something like the following in your main IMessageSink (where you already implemented SyncProcessMessage() as described in the question):
Class YourSink: ...
{
// ... rest of implementation ...
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) {
//[HERE YOU CAN CHANGE THE ORIGINATING MESSAGE]
System.Console.WriteLine("CALL: AsyncProcessMessage()");
return _nextMsgSink.AsyncProcessMessage(msg,new SomeReplySink (replySink));
}
}The line marked in blue in the following code-fragment creates the new reply sink (SomeReplySink) which will afterwards forward the call to the replySink object.
You can then use a config file like the following to handle async requests as well:
<configuration>
<system.runtime.remoting>
<application>
<channels>
<channel ref="http">
<clientProviders>
<provider type="Somewhere.YourSink, SomeAssembly" />
<formatter ref="soap" />
</clientProviders>
</channel>
</channels>
<!-- rest of the configfile -->
</application>
</system.runtime.remoting>
</configuration>