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