Section 5.2. One-Way Operations


5.2. One-Way Operations

There are cases when an operation has no returned values, and the client does not care about the success or failure of the invocation. To support this sort of fire-and-forget invocation, WCF offers one-way operations. Once the client issues the call, WCF generates a request message, but no correlated reply message will ever return to the client. As a result, one-way operations cannot return values, and any exception thrown on the service side will not make its way to the client. Ideally, once the client calls a one-way method, it should be blocked only for the briefest moment it takes to dispatch the call. However, in reality, one-way calls do not equate asynchronous calls. When one-way calls reach the service, they may not be dispatched all at once, and may be queued up on the service side to be dispatched one at a time, all according to the service's configured concurrency mode behavior. (Chapter 8 will discuss concurrency management and one-way calls in depth.) How many messages (be it one-way operations or request-reply ones) the service is willing to queue up is a product of the configured channel and reliability mode. If the number of queued messages has exceeded the queue's capacity then the client will block, even when issuing a one-way call. However, once the call is queued (which is usually the case), the client is unblocked and can continue executing, while the service processes the operation in the background. All the WCF bindings support one-way operations.

5.2.1. Configuring One-Way Operations

The OperationContract attribute offers the Boolean IsOneWay property:

 [AttributeUsage(AttributeTargets.Method)] public sealed class OperationContractAttribute : Attribute {    public bool IsOneWay    {get;set;}    //More members } 

IsOneWay defaults to false, which means a request-reply operation (hence the WCF default). However, setting IsOneWay to TRue configures the method as a one-way operation:

 [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MyMethod( ); } 

There is nothing special or different the client has to do when invoking a one-way operation. The value of the IsOneWay property is reflected in the service metadata. Note that both the service contract definition and the definition imported by the client must have the same value for IsOneWay.

Because there is no reply associated with a one-way operation, there is no point in having any returned values or results. For example, here is an invalid definition of a one-way operation that returns a value:

 //Invalid contract [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    int MyMethod( ); } 

In fact, WCF enforces this by verifying the method signature when loading up the host, and throwing an InvalidOperationException in the case of a mismatch.

5.2.2. One-Way Operations and Reliability

The fact that the client does not care about the result of the invocation does not mean that the client does not care whether the invocation took place at all. In general, you should turn on reliability for your services, even for one-way calls. This will ensure delivery of the requests to the service. However, with one-way calls, the client may or may not care about the invocation order of the one-way operations. This is one of the main reasons why WCF allows you to separate enabling reliable delivery from enabling ordered delivery and execution. Obviously, both the client and the service have to agree beforehand on these details, otherwise the binding configuration will not match.

5.2.3. One-Way Operations and Sessionful Services

WCF will let you design a sessionful contract with one-way operations:

 [ServiceContract(SessionMode = SessionMode.Required)] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MyMethod( ) } 

If the client issues a one-way call, and then closes the proxy while the method executes, the client is blocked until the operation completes.

However, I believe that in general, one-way operations in a sessionful contract indicate bad design. The reason is that having a session usually implies that the service manages state on behalf of the client. Any exception that happens will be likely to fault that state, and yet, the client may be unaware of it. In addition, typically the client (or the service) will choose a sessionful interaction because the contract used requires some lock-step execution advancing through some state machine. One-way calls do not fit this model very well. Consequently, I recommend that one-way operations should be applied on per-call or singleton services only.

If you employ one-way operations on a sessionful contract, strive to have only the last operation terminating the session as a one-way operation (make sure it complies with one-way rules, such as a void return type). You can use demarcating operations to enforce that:

 [ServiceContract(SessionMode = SessionMode.Required)] interface IOrderManager {    [OperationContract]    void SetCustomerId(int customerId);    [OperationContract(IsInitiating = false)]    void AddItem(int itemId);    [OperationContract(IsInitiating = false)]    decimal GetTotal( );    [OperationContract(IsOneWay = true,IsInitiating = false,                                       IsTerminating = true)]    void ProcessOrders( ); } 

5.2.4. One-Way Operations and Exceptions

It is wrong to perceive a one-way operation as a one-way street or a "black hole" that nothing can come out of. First, when dispatching a one-way operation, any error in trying to dispatch the call because of communication problems (such as a wrong address or the host being unavailable) will throw an exception on the client side trying to invoke the operation. Second, depending on the service instance mode and the binding used, the client may be affected by service-side exceptions. The following discussion assumes that the service does not throw a FaultException or a derived exception, as discussed in Chapter 6.

5.2.4.1. Per-call services and one-way exceptions

In the case of a per-call service, when there is no transport session (such as when using the BasicHttpBinding or the WSHttpBinding without reliable messaging and security), if an exception takes place when invoking a one-way operation, the client is unaffected and can continue to issue calls on the same proxy instance:

 [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MethodWithError( );    [OperationContract]    void MethodWithoutError( ); } class MyService : IMyContract {    public void MethodWithError( )    {      throw new Exception( );    }    public void MethodWithoutError( )    {} } //Client side when using basic binding: MyContractClient proxy = new MyContractClient( ); proxy.MethodWithError( ); proxy.MethodWithoutError( ); proxy.Close( ); 

However, when using the WSHttpBinding with security or the NetTcpBinding without reliable messaging or the NetNamedPipeBinding, a service-side exception, including those thrown by one-way operations, will fault the channel, and the client will not be able to issue any new calls using the same proxy instance:

 [ServiceContract] interface IMyContract {    [OperationContract(IsOneWay = true)]    void MethodWithError( );    [OperationContract]    void MethodWithoutError( ); } class MyService : IMyContract {    public void MethodWithError( )    {       throw new Exception( );    }    public void MethodWithoutError( )    {} } //Client side when using TCP or IPC binding: MyContractClient proxy = new MyContractClient( ); proxy.MethodWithError( ); try {    proxy.MethodWithoutError( ); //Will throw because channel is at fault    proxy.Close( ); } catch {} 

The client will not even be able to safely close the proxy.

When using the WSHttpBinding or the NetTcpBinding with reliable messaging, the exception will not fault the channel and the client can continue to issue more calls.

I find these inconsistencies to be disturbing to say the least, first because the choice of a binding should not affect the client code, but also because it is a violation of the semantic of one-way operations, enabling the caller to discover that something went wrong on the service during a one-way invocation.

A sessionless singleton in this respect behaves similarly to the per-call service.


5.2.4.2. Sessionful services and one-way exceptions

The situation is even more complex when it comes to sessionful services throwing an exception in a one-way method. With NetTcpBinding and the NetNamedPipeBinding, the exception terminates the session; WCF disposes of the service instance and faults the channel. Subsequent operation calls using the same proxy yield CommunicationException (or CommunicationObjectFaultedException, when reliability is enabled on the TCP and WS bindings) because there is no longer a session or a service instance. If Close( ) is the only method called on the proxy after the exception, Close( ) tHRows CommunicationException (or CommunicationObjectFaultedException). If the client closes the proxy before the error takes place, Close( ) is blocked until the error takes place, then Close( ) tHRows the exception. This intricate behavior is yet another reason to avoid one-way calls on a sessionful service.

In the case of WS bindings with a transport session, the exception faults the channel, and the client cannot issue new calls on the proxy. Closing the proxy immediately after the call that threw the exception behaves similar to the other bindings.

A sessionful singleton in this respect behaves similarly to the per-session service.





Programming WCF Services
Programming WCF Services
ISBN: 0596526997
EAN: 2147483647
Year: 2004
Pages: 148
Authors: Juval Lowy

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net