Introduction
In this article we will discuss the various message exchange patterns that the WCF service supports. We
will also discuss how we can invoke the WCF service functions asynchronously. We
will try to see each one of these message exchange techniques in details and try to see a sample application
for each one of them.
Background
Calling a function from the application code always follows a simple Request-Response pattern i.e. we call the
function, for the time the function is being executed the caller will wait for the function(block) and once the
function call returns the calling code with become responsive again.
Now this simple request-response is ok for the functions that takes very little time to execute. But if the
function being called takes some considerable time to execute then the calling code will be unresponsive for that
period. This will pose serious problems for the applications that require user interactions.
In case of simple libraries, we can invoke the functions asynchronously and let our application be responsive and
then handle the response from the function separately. From a WCF service perspective, this async function call model
is also allowed but WCF also provides some message exchange patterns out of the box to provide these kind of functionality.
Before looking at the message exchange pattern let us think about the scenarios that we might need from a service consumers perspective.
- Scenario 1: We have some function calls that are not taking too much time and letting the
application wait for such functions is totally acceptable.
-
Scenario 2: We have some function call which is time consuming but calling that function is more like
a trigger from our application code. We just want to call the function and don't want to wait for the
response from the function.
- Scenario 3: We have a long running function, we want to call the function in such a way that the function
call is not blocking and at the same time we want something out of the function(response) into our application.
- Scenario 4: We want a two way communication mechanism(like events) where we hook up our client with the
service, call a function and return back. The service can in turn call a callback function to let the application
know about the response.
Now WCF provides the possibility of configuring our service and service proxy in such a way that all these
scenarios are possible to implement. From the Message Exchange Patterns perspective, WCF provides support for
1st, 2nd and 4th scenario. To have the 3rd scenario working we need to implement the async function calls at the
client side proxy.
Message Exchange Patterns
WCF provides 3 message exchange patterns:
Request/Response
One Way
Duplex
The Request/Reponse pattern is the default and is most widely used pattern. In this pattern the application
waits/blocks till the service function call is complete(Scenario 1). Default OperationContract
behavior is
Request/Response only but if we need to specify it explicitly then we have to configure the IsOneWay
property of
OperationContract
to false
.
One way specifies that the function call will not be blocking. The client will simply call the function and
the control will return to the caller(Scenario 2). To make a function One way complaint we will have to set the IsOneWay
property of OperationContract
to true
.
Duplex provides us with the possibility of having a callback function/object hooked up with the service(Scenario 4).
This will require having an extra interface that the client will need to implement. If together with this
callback interface, we mark the function call as IsOneWay=true
then the function call will be non
blocking and the WCF service will send the callback to the client once the operation is complete. This pattern
is also important when the client wishes to receive some state change information from the service even when it
has not called any operation on the service(More like publisher subscriber model).
Now in some way we can say that the Scenario 3 problem can also be addressed using the Duplex service. But
having a duplex service has some limitations and it is not exactly calling the operations asynchronously. To solve
the Scenario 3 problem, we can call the long running function asynchronously(we will look into it shortly).
Using the code
Let us now create a simple WCF service that will expose 3 functions. One function to be called in Request/Reponse
mode i.e. with IsOneWay=false
. Second function to be called using One-Way pattern i.e. setting the IsOneWay=true
.
And finally a third function that will call the callback on the client side to notify that the call is complete.
Now since we need to demonstrate the Duplex service behavior too, let us create a duplex service only. The first two functions will work in the same fashion in a non duplex service too. Only the third function will utilize the duplex service benefits. Now the steps to create a duplex service are:
- Create the service contract: We will create a simple service contract
ISampleService
. - Create a callback contract: We will create a simple interface called
ICallBack
as callback contract. - Attach callback contract with service contract: we do this by specifying property
CallbackContract=typeof(ICallBack)
in the service contract. - Expose the service: using wsDualHttpBinding.
- Implement the callback contract: We will create a simple class called
CallbackHandler
and implement the callback contract interface(code can be found below). - Create an
InstanceContext
and pass the callback contract instance: This is also done on client side(code can be found below). - Pass the
InstanceContext
object in the proxy class' constructor: This is also done in client code(code can be found below).
Let us now see the implementation of this duplex service and then we will see the client implementation to consume this duplex service.
[ServiceContract(CallbackContract=typeof(ICallBack))]
public interface ISampleService
{
[OperationContract]
void TestFunction1();
[OperationContract(IsOneWay=true)]
void TestFunction2();
[OperationContract(IsOneWay = true)]
void TestFunction3();
}
[ServiceContract]
public interface ICallBack
{
[OperationContract(IsOneWay=true)]
void NotifyClient(string message);
}
The above code snippet shows the service contract and operation contract. Now this service is a Duplex
service and for that we have configured the CallBackContract
property of service contract to point to the callback
interface that we have defined.
Note: TestFunction1
and TestFunction2
will not utilize the Duplex service behaviors. They will work in the same way
even if the service is not a Duplex service. So we can test the other two modes without any problem.
Now the implementation of these functions will simulate some long running process. We do this by sleeping for
5 seconds.
public class SampleService : ISampleService
{
public void TestFunction1()
{
Thread.Sleep(5000);
}
public void TestFunction2()
{
Thread.Sleep(5000);
}
public void TestFunction3()
{
Thread.Sleep(5000);
ICallBack callBack = OperationContext.Current.GetCallbackChannel<ICallBack>();
callBack.NotifyClient("TestFunction3 has been successfully executed");
}
}
This service will now be exposed using the wsDualHttpBinding
binding because this is the binding that
will support duplex mode of communication.
Test Service Client
Let us create a simple windows application so that we can see whether or not the GUI is responsive on making
function calls. This will be a simple application which will have separate buttons to invoke each function. The
response from the function will be shown on the UI.
We will add the service reference into this client application and will test each function call one by one.
Request/Response
The TestFunction1
is configure in request-response mode i.e. IsOneWay
is set to false(the default). The
code to call this function using the client proxy is:
InstanceContext instance = new InstanceContext(new CallbackHandler());
ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient(instance);
listBox1.Items.Add("Request/Response: Calling the WCF service function.");
client.TestFunction1();
listBox1.Items.Add("Request/Response: WCF service function call complete.");
client.Close();
Calling this function from the client will make the UI to hang for 5 seconds
because the calling code is waiting for the function call to finish.
One Way
The TestFunction2
is configure in One way mode i.e. IsOneWay
is set to true. The
code to call this function using the client proxy is:
InstanceContext instance = new InstanceContext(new CallbackHandler());
ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient(instance);
listBox1.Items.Add("One way: Calling the WCF service function.");
client.TestFunction2();
listBox1.Items.Add("One way: WCF service function call complete.");
client.Close();
Calling this function from the client will not make the UI non responsive
because the calling code is not waiting for the function call to finish. It simply calls the function and
returns back assuming that the function will do its bit.
Note: The InstanceContext
mode is needed because we created a Duplex service. If we would have not created a
duplex service then it was not required and the parameter-less constructor of SampleServiceClient
would
have been called instead. In case of non duplex services only this change will be required and the code
will work fine.
Duplex
Now to have the possibility of receiving the callbacks from the service, we first need to implement the
callback interface of the service.
class CallbackHandler : ServiceReference1.ISampleServiceCallback
{
public void NotifyClient(string message)
{
MessageBox.Show(message);
}
}
This callback class' instance will be passed inside the InstanceContext
and this InstanceContext
will
be passed to the service so that the service can establish a connection with the client and call the callback
functions. Calling the function is same but we need to make the SampleServiceClient
a class
level member variable so that the instance is alive when the service sends the callback.
InstanceContext instance = null;
ServiceReference1.SampleServiceClient client = null;
public Form1()
{
InitializeComponent();
instance = new InstanceContext(new CallbackHandler());
client = new ServiceReference1.SampleServiceClient(instance);
}
private void button3_Click(object sender, EventArgs e)
{
listBox1.Items.Add("Duplex: Calling the WCF service function.");
client.TestFunction3();
listBox1.Items.Add("Duplex: WCF service function call complete.");
}
Now calling this function will not block the UI because this function is also marked as IsOneWay=true
.
But since the function implementation calls the callback function of the client the function implementation
NotifyClient
will be called once the function call is complete(after 5 seconds) and will show us the message box.
Calling the Operations Asynchronously
Now if we have a long running process which is neither marked as one way operation nor it is possible to
have a duplex service, then the only option from the clients perspective is to call this method asynchronously.
Calling the methods asynchronously gives us the possibility of having responsive caller code with the proper response
message being also getting captured in the client.
The first thing that we need to do to implement asynchronous method call is to configure the proxy to generate
the support for async functions.
Now this gives us the async version of all the functions along with the callback events that will be called
when the function call is finished. So let us call the Function1
again but this time asynchronously
InstanceContext instance = new InstanceContext(new CallbackHandler());
ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient(instance);
listBox1.Items.Add("Request/Response: Calling the WCF service function async");
client.TestFunction1Completed += new EventHandler<AsyncCompletedEventArgs>(client_TestFunction1Completed);
client.TestFunction1Async();
listBox1.Items.Add("Request/Response: WCF service function async call complete.");
client.Close();
Upon completion the event handler will be called and show us the message box indicating that the function call
has been complete.
Note: This is the client side implementation of async pattern where the client application call the operation in a separate thread and manages the communication with main thread. We can also create async operations on WCF service where a separate thread is created inside the service itself. This is done by setting the AsyncPattern
property of OperationContract
to true
. But this itself is considerable topic and perhaps need a separate discussion altogether. Talking about it in this article will make this article little digressing.
Some important points
The request response pattern is the most common and widely used pattern. If we are using some other pattern then
there are some limitations that comes with them. Let us see these limitations before deciding upon the pattern to use
in our WCF service.
One Way: When we use One way pattern once the function is called there is no link between the caller and the callee.
This is ok because we needed this behavior. But the catch here is that the caller has no way of knowing
whether the function call was success or failure. We cannot propagate any faults back to the client because the link
between client and the service will no longer exist when the function execution is in progress.
Duplex: Duplex service works on some limited bindings. Not all the bindings support
duplex communication. Duplex services need to have a connection between the client and the server.
This is not always possible
due to network design limitations. The Duplex service can also rise some subtle threading relation issues which
might be hard to fix or perhaps cause some unexpected behavior.
Point of interest
In this article, we have seen all the three possible message exchange patterns supported by the WCF service and how
we can call the operations asynchronously from the client. The code snippets shown in this article are only showing the
bare minimum code needed in the context. To get the full understanding looking at the sample application is recommended.
I hope this has been informative.
History
-
30 March 2013: First version.