使用会话

在 Windows Communication Foundation(WCF)应用程序中,会话 将一组消息关联到对话中。 WCF 会话不同于 ASP.NET 应用程序中提供的会话对象,支持不同的行为,并且以不同的方式控制。 本主题介绍会话在 WCF 应用程序中启用的功能以及如何使用这些功能。

Windows Communication Foundation 应用程序中的会话

当服务协定指定它需要会话时,该协定将指定所有调用(即支持调用的基础消息交换)必须是同一会话的一部分。 如果协定指定它允许会话,但不需要会话,客户端可以连接并建立会话,或者不建立会话。 如果会话结束,然后在同一个通道上发送消息,将会引发异常。

WCF 会话具有以下主要概念性功能:

  • 它们由调用应用程序(WCF 客户端)显式启动和终止。

  • 会话期间传递的消息按接收顺序进行处理。

  • 会话将一组消息相互关联,从而形成对话。 可以采用不同类型的关联。 例如,一个基于会话的通道可能会基于共享网络连接关联消息,而另一个基于会话的通道可能会基于消息正文中的共享标记关联消息。 可以从会话派生的功能取决于关联的性质。

  • 没有与 WCF 会话关联的常规数据存储。

如果你熟悉 System.Web.SessionState.HttpSessionState ASP.NET 应用程序中的类及其提供的功能,你可能会注意到该会话和 WCF 会话之间的以下差异:

  • ASP.NET 会话始终是服务器发起的。

  • ASP.NET 会话本质上是无序的。

  • ASP.NET 会话提供跨请求的常规数据存储机制。

本主题介绍了:

  • 在服务模型层中使用基于会话的绑定时的默认执行行为。

  • 基于 WCF 会话的、系统提供的绑定所提供的功能的类型。

  • 如何创建声明会话要求的协定。

  • 如何了解和控制会话的创建和终止,以及会话与服务实例的关系。

使用会话的默认执行行为

尝试启动会话的绑定称为 基于会话的 绑定。 服务协定通过将服务协定接口(或类)上的属性设置为 ServiceContractAttribute.SessionMode 枚举值之一来指定它们需要、允许或拒绝基于会话的 System.ServiceModel.SessionMode 绑定。 默认情况下,此属性的值是 Allowed,这意味着,如果客户端使用基于会话的绑定和 WCF 服务实现,该服务将建立和使用提供的会话。

当 WCF 服务接受客户端会话时,默认情况下会启用以下功能:

  1. WCF 客户端对象之间的所有调用都由同一服务实例处理。

  2. 不同的基于会话的绑定提供了其他功能。

System-Provided 会话类型

基于会话的绑定支持服务实例与特定会话的默认关联。 但是,除了启用前面所述的基于会话的实例控件之外,不同的基于会话的绑定还支持不同的功能。

WCF 提供以下类型的基于会话的应用程序行为:

SessionMode属性设置不指定合约所需的会话类型,只是指出它需要一个会话。

创建一个需要会话的协定

创建要求会话的合同说明,服务合同声明的操作组必须在同一会话内执行,并且消息必须按顺序传递。 为了确定服务协定所需的会话支持级别,请在服务协定接口或类上将 ServiceContractAttribute.SessionMode 属性设置为 System.ServiceModel.SessionMode 枚举的值,以明确协定是否:

  • 需要会话。

  • 允许客户端建立会话。

  • 禁止会话。

但是,设置属性 SessionMode 并不指定协定所需的基于会话的行为的类型。 它指示 WCF 在运行时确认:在实现服务时,为服务配置的绑定(用于创建通信通道)是执行会话、不执行会话还是可以建立会话。 同样,绑定可以使用它选择的任何类型的基于会话的行为来满足该要求:安全性、传输、可靠或某种组合。 确切行为取决于 System.ServiceModel.SessionMode 所选的值。 如果服务的已配置绑定不符合其值 SessionMode,则会引发异常。 绑定及其创建的支持会话的通道可认为是基于会话的。

下面的服务协定指定 ICalculatorSession 中的所有操作必须在会话中进行交换。 除了 Equals 方法外,任何操作都不会向调用方返回值。 但是,该方法 Equals 不接受任何参数,因此只能在会话中返回一个非零值,在该会话中,数据已传递到其他操作。 此合同要求会话能够正常运作。 如果没有与特定客户端关联的会话,则服务实例无法知道此客户端发送的以前数据。

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface ICalculatorSession
{
    [OperationContract(IsOneWay=true)]
    void Clear();
    [OperationContract(IsOneWay = true)]
    void AddTo(double n);
    [OperationContract(IsOneWay = true)]
    void SubtractFrom(double n);
    [OperationContract(IsOneWay = true)]
    void MultiplyBy(double n);
    [OperationContract(IsOneWay = true)]
    void DivideBy(double n);
    [OperationContract]
    double Equals();
}
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required)> _
Public Interface ICalculatorSession

    <OperationContract(IsOneWay:=True)> _
    Sub Clear()
    <OperationContract(IsOneWay:=True)> _
    Sub AddTo(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub SubtractFrom(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub MultiplyBy(ByVal n As Double)
    <OperationContract(IsOneWay:=True)> _
    Sub DivideBy(ByVal n As Double)
    <OperationContract()> _
    Function Equal() As Double
End Interface

如果服务允许会话,则建立会话,并在客户端启动会话时使用;否则,不会建立任何会话。

会话和服务实例

如果在 WCF 中使用默认实例化行为,则 WCF 客户端对象之间的所有调用都由同一服务实例处理。 因此,在应用程序级别上,可以将会话视为启用与本地调用行为相似的应用程序行为。 例如,创建本地对象时:

  • 调用构造函数。

  • 对 WCF 客户端对象引用进行的所有后续调用均由同一对象实例处理。

  • 在销毁对象引用时调用析构函数。

只要使用默认服务实例行为,会话就可以在客户端和服务之间启用类似的行为。 如果服务协定需要或支持会话,可以通过设置 IsInitiatingIsTerminating 属性将一个或多个协定作标记为启动或终止会话。

启动操作 是必须作为新会话中的第一个调用的操作。 只有在调用了至少一个启动操作后,才能调用非发起操作。 因此,可以通过声明一个启动操作来为服务创建某种会话构造函数,启动操作应设计为从与服务实例的开始相对应的客户端接收输入。 (但是,状态与会话相关联,而不是服务对象)。

相反,终止操作必须作为现有会话中的最后一条消息被调用。 在默认情况下,WCF 在关闭服务关联的会话后回收服务对象及其上下文。 因此,可以通过声明终止操作来创建某种析构函数,终止操作应设计为执行与服务实例的结束相对应的函数。

注释

尽管默认行为与本地构造函数和析构函数相似,但它只是一种相似之处。 任何 WCF 服务操作都可以是启动或终止操作,也可以同时具有这两种操作。 此外,在默认情况下,可以按任意顺序多次调用启动操作;一旦会话建立并与实例关联,就不会创建其他会话,除非您显式控制服务实例的生存期(通过操作 System.ServiceModel.InstanceContext 对象)。 最后,状态与会话相关联,而不是服务对象。

例如,前面的示例中使用的 ICalculatorSession 协定要求 WCF 客户端对象在任何其他操作之前首先调用 Clear 操作,并且与此 WCF 客户端对象的会话应在调用 Equals 操作时终止。 下面的代码示例演示了强制实施这些要求的协定。 Clear 必须先被调用才能启动会话,调用 Equals 后该会话结束。

[ServiceContract(Namespace="http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface ICalculatorSession
{
    [OperationContract(IsOneWay=true, IsInitiating=true, IsTerminating=false)]
    void Clear();
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void AddTo(double n);
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void SubtractFrom(double n);
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void MultiplyBy(double n);
    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void DivideBy(double n);
    [OperationContract(IsInitiating = false, IsTerminating = true)]
    double Equals();
}
<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples", SessionMode:=SessionMode.Required)> _
Public Interface ICalculatorSession

    <OperationContract(IsOneWay:=True, IsInitiating:=True, IsTerminating:=False)> _
    Sub Clear()
    <OperationContract(IsOneWay:=True, IsInitiating:=False, IsTerminating:=False)> _
    Sub AddTo(ByVal n As Double)
    <OperationContract(IsOneWay:=True, IsInitiating:=False, IsTerminating:=False)> _
    Sub SubtractFrom(ByVal n As Double)
    <OperationContract(IsOneWay:=True, IsInitiating:=False, IsTerminating:=False)> _
    Sub MultiplyBy(ByVal n As Double)
    <OperationContract(IsOneWay:=True, IsInitiating:=False, IsTerminating:=False)> _
    Sub DivideBy(ByVal n As Double)
    <OperationContract(IsInitiating:=False, IsTerminating:=True)> _
    Function Equal() As Double
End Interface

服务不会启动与客户端的会话。 在 WCF 客户端应用程序中,基于会话的通道的生存期与会话本身的生存期之间存在直接关系。 因此,客户端通过创建新的会话通道来启动新会话,并通过顺利关闭会话通道来结束现有会话。 客户端通过调用以下项之一来启动与服务终结点的会话:

通常,客户端通过调用以下项之一来结束与服务终结点的会话:

有关示例,请参阅 如何:创建需要会话的服务 以及 默认服务行为实例化 示例。

有关客户端和会话的详细信息,请参阅 使用 WCF 客户端访问服务

会话与 InstanceContext 设置交互

在契约中,SessionMode枚举与ServiceBehaviorAttribute.InstanceContextMode属性之间存在相互作用,它控制通道与特定服务对象之间的关联。 有关详细信息,请参阅 会话、实例化和并发

共享 InstanceContext 对象

通过自己执行关联,您还可以控制将哪个基于会话的通道或调用与哪个 InstanceContext 对象相关联。

会话和流

在需要传输大量数据时,WCF 中的流传输模式是一个可行的选择,它可以替代将消息全部缓存在内存中进行处理的默认行为。 在流与基于会话的绑定一起调用时可能会产生意外行为。 可通过单一通道(数据报通道)执行所有流调用,该通道不支持会话,即使将正在使用的绑定配置为使用会话也是如此。 如果多个客户端通过基于会话的绑定对同一服务对象进行流式处理调用,并且服务对象的并发模式设置为单个,并且其实例上下文模式设置为 PerSession单个,则所有调用都必须通过数据报通道进行,因此每次只处理一个调用。 一个或多个客户端因此可能会超时。通过将该服务对象的 InstanceContextMode 设置为 PerCall 或将“并发”设置为“多个”,即可解决此问题。

注释

在这种情况下,MaxConcurrentSessions 不起作用,因为只有一个“会话”可用。

另请参阅