WCF提供了单向操做,一旦客户端调用,WCF会生成一个请求,但没有相关的应答信息返回给客户端。因此,单向操做是不能有返回值,服务抛出的任何异常都不会传递给客户端。安全
理想状况下,一旦客户端调用了一个单向操做,它只会在要求调用的一瞬间被阻塞。事实上,单向调用不等于异步调用。当单向调用到达服务端时,不会当即分发这些调用,而是将调用方法服务端的队列中,并在某个时间分发。这一过程要根据服务配置的并发模式行为而定。服务要放入到队列中的消息个数与哦诶只的管道及可靠性模式有关。若是队列消息的数量超过了队列的容量,即便发出的只是单向调用,也会阻塞客户端。然而,一旦调用被放入队列中,就会取消对客户端的阻塞,继续执行。同时,服务会在后台处理这一操做。多线程
开发人员容易错误的认为单向操做等于并发调用。若是客户端使用了相同的代理,但却利用了多线程调用单向操做,则在服务端的调用多是并发,也可能不是。从本质上将,这种交互式是由服务并发管理模式和传输会话所决定的。并发
全部的WCF绑定都支持单向操做。异步
OperationContract特性定义了Boolean类型的IsOneWay属性:ui
public sealed class OperationContractAttribute : Attribute { public bool IsOneWay { get; set; } }IsOneWay属性的默认值为false,即默认操做为请求/应答操做,若是将IsOneWay属性设置为true,方法就会成为单向操做。spa
[ServiceContract] public interface IService7 { [OperationContract(IsOneWay=true)] void MyMethod(); }调用单向操做时,客户端并没有任何特别之初。IsOneWay属性的值会包含在服务元数据中。注意,服务契约定义与客户端导入定义中的IsOneWay的值是相同的。线程
因为单向操做没有应答消息,所以它不能包含返回值。以下所示:设计
[ServiceContract] public interface IService7 { [OperationContract(IsOneWay=true)] void MyMethod(); }事实上,在加载宿主或打开代理时,WCF会强制要求验证方法的签名。代理
客户端不关心调用的结果,并不意味着它不关心调用是否发生。总而言之,即便采用了单向操做,也必须保证服务的可靠性,使他可以确保请求正确的传递到服务。code
但客户端不会考虑单向操做的调用顺序,这也是WCF容许开发者将有效的可靠性传递从有效的有序传递与消息的执行中分离出来的主要缘由。
WCF容许开发者设计一个具备单向操做的会话契约:
[ServiceContract(SessionMode=SessionMode.Required)] public interface IService7 { [OperationContract(IsOneWay=true)] void MyMethod(); }在这种配置下,若是客户端发出一个单向操做,则在执行方法时会关闭代理,而后阻塞客户端直到操做完成。
虽然技术上可行,可是在一个会话契约中包含一个单向操做是一种糟糕的设计。由于拥有一个会话每每意味着服务须要管理表明了客户端的状态。任何异常均可能破坏这个状态,而这时的客户端取没法获取异常。此外,客户端或服务之因此选择一个会话交互,是由于它使用的契约须要经过某个状态机 完成锁步执行。单调操做没法知足这种模式的要求。因此建议只能将单向操做应用到单向服务或者单例服务中。
若是在会话契约中定义了单向操做,就必须保证单向操做是终止会话的最后一个操做。这个能够经过分布操做来实现:
[ServiceContract(SessionMode=SessionMode.Required)] public interface IService7 { [OperationContract()] void MyMethod(); [OperationContract(IsOneWay = true,IsInitiating=false,IsTerminating=true)] void Over(); }
即便单向操做没有返回值,也不会从服务端返回异常,但客户端仍然须要得到单向调用的异常,设置推断调用在服务段已经失败。
若是没有传输会话(使用BasicHttpBingding绑定或不包含可靠消息传输与安全的WsHttpBingding绑定),在调用一个单向操做期间,若是发生异常,客户端并不会受到影响,会继续发出对相同代理实例的调用:
[ServiceContract] public interface IService7 { [OperationContract(IsOneWay =true)] void MethodWithError(); [OperationContract] void MethodWithoutError(); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)] public class Service7:IService7 { public void MethodWithError() { throw new Exception(); } public void MethodWithoutError() { } } //不具备传输会话 MyContractClient proxy = new MyContractClient(); proxy.MethodWithError(); proxy.MethodWithoutError(); proxy.Close();若是存在传输会话,那么一个服务端的异常(包含单向操做抛出的异常)就会操做通道错误。此时,客户端不能发出任何一个使用相同代理实例的新的调用。
[ServiceContract] public interface IService7 { [OperationContract(IsOneWay =true)] void MethodWithError(); [OperationContract] void MethodWithoutError(); } [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)] public class Service7:IService7 { public void MethodWithError() { throw new Exception(); } public void MethodWithoutError() { } } //具备传输会话 MyContractClient proxy = new MyContractClient(); proxy.MethodWithError(); try { proxy.MethodWithoutError();//由于通道错误,从而会被抛出 proxy.Close(); } catch {}客户端甚至不能安全地关闭代理。所以,单向操做并不具有即发即弃的特性,由于客户端在调用期间会发现服务端出现的错误。
之后会展现如何真正的异步即发即弃的操做使用单向操做。