One-Way Operations

There are cases when an operation has no return value, 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 exceptions thrown on the service side will not make their way to the client.

Ideally, when the client calls a one-way method, it should be blocked only for the briefest moment required to dispatch the call. However, in reality, one-way calls do not equate to asynchronous calls. When one-way calls reach the service, they may not be dispatched all at once but may instead be queued up on the service side to be dispatched one at a time, according to the service's configured concurrency mode behavior. (Chapter 8 will discuss concurrency management and one-way calls in depth.) How many messages the service is willing to queue up (be they one-way or request-reply operations) is a product of the configured channel and reliability mode. If the number of queued messages has exceeded the queue's capacity, the client will be blocked even if it's issued a one-way call. However, once the call is queued, the client will be unblocked and can continue executing, while the service processes the operation in the background.

It's also wrong to equate one-way calls with concurrent calls. If the client uses the same proxy yet utilizes multiple threads to invoke one-way calls, the calls may or may not execute concurrently on the service, and the exact nature of the interaction will be determined by the service concurrency management mode and the transport session (see Chapter 8 for more on this subject).

All of the WCF bindings support one-way operations.

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( );
}

The client doesn't have to do anything special or different 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 the host or opening the proxy and throwing an InvalidOperationException in the case of a mismatch.

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, 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 of messages. Obviously, both the client and the service have to agree beforehand on these details, or the binding configuration will not match.

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( );
}

With this configuration, if the client issues a one-way call and then closes the proxy while the method executes, the client will still be blocked until the operation completes.

While technically possible, I believe that in general one-way operations in a sessionful contract (and per-session instantiation) 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 may happen 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 do employ one-way operations on a sessionful contract, strive to make only the last operation terminating the session a one-way operation (and make sure it complies with one-way rules, such as having 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( );
}

One-Way Operations and Exceptions

Although one-way operations do not return values or exceptions from the service itself, it's wrong to perceive them as a one-way street or a "black hole" from which nothing can come out. The client should still expect exceptions from a one-way call, and can even deduce that the call failed on the service. When dispatching a one-way operation, any error because of communication problems (such as a wrong address or the host being unavailable) will throw an exception on the side of the client trying to invoke the operation. Furthermore, 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.)

When there is no transport session (for example, when using the BasicHttpBinding or the WSHttpBinding without reliable messaging and security), if an exception takes place during the invocation of 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 without transport session:
MyContractClient proxy = new MyContractClient( );
proxy.MethodWithError( );
proxy.MethodWithoutError( );
proxy.Close( );

However, in the presence of a transport session, a service-side exception—including one thrown by a one-way operation—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 with transport session
MyContractClient proxy = new MyContractClient( );
proxy.MethodWithError( );
try
{
   proxy.MethodWithoutError( ); //Will throw because channel faulted
   proxy.Close( );
}
catch
{}

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

I find these inconsistencies 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 semantics of true one-way operations, enabling the caller to discover that something went wrong on the service during a one-way invocation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.246.245