One-Way Services

The default behavior of a service operation is the request-reply pattern. In a request-reply pattern, the client waits for the reply message, even if the service operation is represented in code as a void method. With a one-way operation, only one message is transmitted. The receiver does not send a reply message, nor does the sender expect one.

Use the one-way design pattern:

When an operation is one-way, there is no response message to carry error information back to the client. You can detect error conditions by using features of the underlying binding, such as reliable sessions, or by designing a duplex service contract that uses two one-way operations—a one-way contract from the client to the service to call service operation and another one-way contract between the service and the client so that the service can send back faults to the client using a callback that the client implements.

To create a one-way service contract, define your service contract, apply the OperationContractAttribute class to each operation, and set the IsOneWay property to true, as shown in the following sample code.

public interface IOneWayCalculator  
    void Add(double n1, double n2);  
    [OperationContract(IsOneWay = true)]  
    void Subtract(double n1, double n2);  
    [OperationContract(IsOneWay = true)]  
    void Multiply(double n1, double n2);  
    [OperationContract(IsOneWay = true)]  
    void Divide(double n1, double n2);  

For a complete example, see the One-Way sample.

Clients Blocking with One-Way Operations

It is important to realize that while some one-way applications return as soon as the outbound data is written to the network connection, in several scenarios the implementation of a binding or of a service can cause a WCF client to block using one-way operations. In WCF client applications, the WCF client object does not return until the outbound data has been written to the network connection. This is true for all message exchange patterns, including one-way operations; this means that any problem writing the data to the transport prevents the client from returning. Depending upon the problem, the result could be an exception or a delay in sending messages to the service.

For example, if the transport cannot find the endpoint, a System.ServiceModel.EndpointNotFoundException exception is thrown without much delay. However, it is also possible that the service is unable to read the data off the wire for some reason, which prevents the client transport send operation from returning. In these cases, if the Binding.SendTimeout period on the client transport binding is exceeded, a System.TimeoutException is thrown—but not until the timeout period has been exceeded. It is also possible to fire so many messages at a service that the service cannot process them past a certain point. In this case, too, the one-way client blocks until the service can process the messages or until an exception is thrown.

Another variation is the situation in which the service ServiceBehaviorAttribute.ConcurrencyMode property is set to Single and the binding uses sessions. In this case, the dispatcher enforces ordering on the incoming messages (a requirement of sessions), which prevents subsequent messages from being read off the network until the service has processed the preceding message for that session. Again, the client blocks, but whether an exception occurs depends upon whether the service is able to process the waiting data prior to the timeout settings on the client.

You can mitigate some of this problem by inserting a buffer between the client object and the client transport's send operation. For example, using asynchronous calls or using an in-memory message queue can enable the client object to return quickly. Both approaches may increase functionality, but the size of the thread pool and the message queue still enforce limits.

It is recommended, instead, that you examine the various controls on the service as well as on the client, and then test your application scenarios to determine the best configuration on either side. For example, if the use of sessions is blocking the processing of messages on your service, you can set the ServiceBehaviorAttribute.InstanceContextMode property to PerCall so that each message can be processed by a different service instance, and set the ConcurrencyMode to Multiple in order to allow more than one thread to dispatch messages at a time. Another approach is to increase the read quotas of the service and client bindings.

See also