一、服务实例的生命周期概览
我们已经直到,通过显式调用Close方法或等待默认的超时时间到来,都可以释放服务实例。但是,在会话连接里,经常需要按一定顺序调用方法。
二、分步操作
会话契约的操作有时隐含了操作调用的顺序。WCF提供了一种被称之为分步操作(Demarcating Operation)的方法,以应对服务契约的操作需要指定执行顺序的情况。分步操作是使用OperationContract特性的IsInitiating和IsTerminating属性:
[AttributeUsage(AttributeTargets.Method)] public sealed class OperationContractAttribute : Attribute { public bool IsInitiating {get;set;} public bool IsTerminating {get;set;} //More members }
IsInitiating属性的默认值为true,IsTerminating属性的默认的属性值则为false,对这些属性设置值会影响生成的WSDL文档。所以以下两个定义是等效的:
[ServiceContract] public interface IService5 { [OperationContract] void MyMethod(); [OperationContract(IsInitiating=true,IsTerminating=false)] void MyMethod(); }
默认情况下,操作不能划分会话的执行顺序。调用操作的顺序可以是最先、最后,也可以在会话中其它操作之间。使用属性的非默认值可以指定一个方法不被最先调用,或被最后调用,或者同时符合两个约束条件。
此时,操作AddItem()、GetTotal()以及ProcessOrder()都不能是启动会话的第一个操作。同时,ProcessOrder()操作则会成为终止会话的操作。这与业务的要求是一脉相承的。[ServiceContract(SessionMode = SessionMode.Required)] interface IOrderManager { [OperationContract] void SetCustomerId(int customerId); [OperationContract(IsInitiating = false)] void AddItem(int itemId); [OperationContract(IsInitiating = false)] decimal GetTotal( ); [OperationContract(IsInitiating = false,IsTerminating = true)] bool ProcessOrders( ); }
如果IsInitiating值为true(默认值),并不必然代表该操作必然是启动会话的第一个操作。如果其它相同设置的操作首先被调用,就会启动一个会话,而该操作则在被调用时才加入会话,成为会话的一部分;如果IsInitiating值为false,那就意味着在新的会话中该操作绝对不能作为第一个操作被客户端调用,同时,该方法只能在被调用时才加入会话,成为会话的一部分。
如果IsTermination的值为true,则代表该操作必须是终止会话的操作,然后以异步的方式释放服务实例。此时,客户端不能通过代理发出另外的调用。注意,客户端仍然应该关闭代理。虽然在服务契约定义时,允许将多个操作的IsTerminating值设置为true,但一旦调用了IsTerminating值为true的方法,就不能再调用服务实例的其它方法,除非在客户端重新创建一个代理对象。
三、实例停止
实例停用只针对会话服务而言。单例服务虽然也可以应用,但却无效。
迄今为止描述的会话服务的实例管理技术能够将一个客户端(多个客户端)连接到服务实例上。然而,真正的情形却复杂得多。先前提到过,每个服务实例都被托管在上下文中,如下图所示:
会话实际要做的不仅是关联客户端消息,同时还要关联托管了服务的上下文。启动宿主会创建一个新的上下文。会话终止时,上下文也随之终止。默认情况下,上下文的生命周期与发布的服务实例的生命周期相同。然而,出于优化和可扩展性的目的,WCF设计者提供了一种分离两种生命周期的选项,该选项允许WCF独立地停止实例,而不必依赖于它的上下文。实际上,WCF还允许不包含实例的上下文存在,如上图所示,我们将这种管理技术称为上下文停止。控制上下文停止的最常见办法是通过设置OperationBehavior特性的ReleaseInstanceMode属性。
public enum ReleaseInstanceMode { None, BeforeCall, AfterCall, BeforeAndAfterCall, } [AttributeUsage(AttributeTargets.Method)] public sealed class OperationBehaviorAttribute : Attribute,... { public ReleaseInstanceMode ReleaseInstanceMode {get;set;} //More members }
ReleaseInstanceMode属性属于ReleaseInstanceMode枚举类型,其值代表了释放与方法相关的实例的时间:调用前,调用后、调用前与调用后,以及调用中。释放实例时,如果服务支持IDisposable接口,则还要停止Dispose()方法,并且Dispose()方法会拥有一个操作上下文。
通常,只需要将实例停止应用到部分(而不是全部)服务方法上,或者为不同的方法设置不同的值。
class MyService : IMyContract,IDisposable { [OperationBehavior(ReleaseInstanceMode = ReleaseInstanceMode.AfterCall)] public void MyMethod( ) {...} public void MyOtherMethod( ) {...} public void Dispose( ) {...} }
配置为ReleaseInstanceMode.None
ReleaseInstanceMode属性的默认值为ReleaseInstanceMode.None。
ReleaseInstanceMode.None意味着实例的生命周期不收调用的影响,如下图所示:
配置为ReleaseInstanceMode.BeforeCall
当方法被配置为配置为ReleaseInstanceMode.None时,如果会话已经存在一个实例,那么在转发调用之前WCF会停止实例,创建一个新的实例取代它,并让这个新的实例维护所要转发的调用,如下图所示:
ReleaseInstanceMode.BeforeCall可以用于优化诸如Create()这样的方法,用这些方法能够获取一些有价值的资源,并且释放原先分配的支援。启动会话时不需要获取资源,而应该等待,直到调用Create()方法,然后释放原先分配的资源,同时获取新的资源。在调用Create方法之后,就应该i准备启动对实例上的其它被配置成ReleaseInstanceMode.None方法的调用。
配置为ReleaseInstanceMode.AfterCall
当方法被配置为配置为ReleaseInstanceMode.AfterCall时,WCF会在调用方法之后停止实例,如下图所示:
这样设计可以优化诸如Cleanup()这样的方法,该方法能够清除实例所持有的有价值的资源,而不需要等到会话结束。通常都会在配置为ReleaseInstanceMode.None方法之后应用ReleaseInstanceMode.AfterCall的方法。
配置为ReleaseInstanceMode.BeforeAndAfterCall
当方法配置为ReleaseInstanceMode.BeforeAndAfterCall时,它结合了ReleaseInstanceMode.BeforeCall和ReleaseInstanceMode.AfterCall的功能。在执行调用之前,如果上下文包含一个实例,那么WCF就会调用前停止实例,再创建一个新的实例去维护调用,然后在调用后停止这个新创建的实例,如下图所示:
显式停止
除了可以通过应用OperationBehaviorAttribute应用ReleaseInstanceMode,也可以通过编程方式在服务的操作中显式地完成对实例的停止。方法是利用InstanceContext的ReleaseServiceInstance()方法:
class MyService : IMyContract,IDisposable { public void MyMethod( ) { //Do some work then OperationContext.Current.InstanceContext.ReleaseServiceInstance( ); } public void Dispose( ) {...} }
这两种实现实例停止的方式还可以结合使用,例如在应用了OperationBehavior特性,并将ReleaseInstanceMode设置为 BeforeCall的方法中,如果显式调用ReleaseServiceInstance()方法,其效果就相当于将 ReleaseInstanceMode设置为BeforeAndAfterCall。
实例停止同样能够影响到单例服务。虽然服务允许这样的定义,但试图停止单例对象的方式是不可取的。
使用实例停止
实例停止是一种优化技术,通常情况下应该尽量避免使用它。只有在无法同时满足系统的性能要求和可伸缩性要求时,才考虑使用实例停止。