WCF 会话 - 简要介绍






4.86/5 (49投票s)
关于 WCF 会话的简要信息
引言
“会话”对我们所有人来说是一个非常容易理解的术语,按照我们的普遍理解,它(或多或少)是指实体相互识别的某个持续时间。我们中的一些人可能在 ASP.NET 中也接触过它。WCF 中的概念非常相似,尽管技术和用法有些不同。
在 WCF 中,总有一个服务类实例来处理传入的服务请求。这些实例可能已经存在(在请求到达时就在服务器上)或按需创建。在 WCF 中,会话的主要概念是管理这些服务实例,以便服务器能够以优化方式得到利用。在服务器端,有一个名为 InstanceContext
的特殊类,它创建/加载服务类实例并将其分派给请求。这种关联可以被理解为
您可以看到这里的运作方式。当请求到达时,它会通过实例上下文路由到服务实例。假设有数千个请求,那么服务将不得不创建数千个实例上下文(这些上下文又会创建数千个服务实例)来处理这些请求。如果请求以这种方式处理,那么该服务称为 **PERCALL** 服务,因为每个请求都由一个新的实例上下文和服务实例对象(后面可称其为服务对象)处理。假设有一个客户端发出了 100 个请求。如果服务能够识别此客户端并始终由专用的服务对象为其服务,那么这种服务将被称为 **PERSESSION** 服务,因为它能够识别客户端并由单个服务对象实例为其服务。另一方面,如果所有请求(无论来自哪个客户端)都由单个服务对象实例处理,那么该服务将被称为 **SINGLETON** 服务。下图总结了这一概念

如何在 WCF 中配置会话?
要配置 WCF 中的会话,您应该了解以下三个元素
Binding
– 因为并非所有绑定都支持会话。只有 **WS-*,**NetTcpBinding
和NetNamedPipeBinding
具有会话支持,因此选择合适的绑定至关重要。SessionMode
– 此服务协定指定了服务对来自传入客户端请求的会话的可能期望。它有三个不言自明的値Allowed
– 服务可以接受有会话的客户端,也可以接受无会话的客户端。Required
– 服务将仅处理有会话的客户端,无会话的客户端无法连接到服务。NotAllowed
– 服务只能与无会话的客户端进行交互。与Required
完全相反。
InstanceContextMode
– 这是一种服务行为,用于控制实际服务类的实例化。它有以下値PerCall
– 对服务器的每个请求都将由一个新的服务类实例处理。PerSession
– 一个会话将拥有其专用的服务实例,并且该会话的所有请求都将由该实例处理。所有会话都将拥有其独立的专用服务实例。Single
– 对服务的所有请求都将由服务类的一个唯一实例处理。
让我们来总结一下。首先,需要有一个合适的绑定来为会话提供支持。然后,服务协定的 SessionMode
应该是支持的,以便服务可以允许有会话的请求,最后,InstanceContextMode
应该配置为以有会话的方式响应。最后一个设置至关重要,因为它决定了请求是由新的服务实例还是由现有服务实例处理。这些设置可以有无数种组合,您只需要判断有意义的组合。例如,如果您指定了支持会话的绑定、Allowed 会话模式,但指定了 Per Call 实例上下文模式,那么这种会话就没有意义了,因为尽管有其他支持,每个请求仍将由一个新的服务类实例处理。
让我们做一些实验
下载附件中的示例代码并遵循本文。将理论与实践结合起来,将使您更好地理解概念。在附件的示例代码中,有两个项目——一个是窗体应用程序,另一个是控制台应用程序。窗体应用程序充当客户端,而控制台应用程序是 WCF 服务器。有两种绑定
- basicHttp 和
- NetTcp
两个按钮处理程序在两种不同的绑定上调用 WCF 服务。在服务端,被调用的操作会分析三个对象——实例上下文、服务上下文和会话 ID。这里的目的是检查如何创建不同的对象和会话 ID 来处理传入的请求。窗体应用程序有一个标签控件,用于显示客户端的会话 ID。
现在运行代码并单击 **Http** 按钮。您会观察到表单和服务端都没有会话 ID,现在单击此按钮两次,看看每次实例上下文和服务对象都是不同的。这种情况描绘了 Per Call 服务,因为服务总是使用新的实例上下文和服务实例。现在单击 TCP 按钮两次,并观察服务端和客户端的值。您会发现会话 ID 在服务端和客户端不相同,但服务仍然由相同的服务对象响应。当单击 **TCP** 按钮时,客户端通过有会话的通道发出请求,服务接受此会话并作为有会话的服务响应,这就是为什么每个请求都由相同的服务对象实例(实例上下文 + 服务实例)处理。
有一点需要理解,与 ASP.NET 不同,WCF 中的会话 ID 不一定是客户端和服务端相同的。这是因为对于 NetTCPBinding
和 NetNamedPipeBinding
这两种绑定,WCF 通过底层的传输通道来识别客户端;但是对于其他一些绑定(如 WSHttp
),它们使用无连接的 HTTP 作为底层通道,WCF 需要某种方式来识别客户端。在这种情况下,它会在消息头中添加一个新的字段——会话 ID 来识别客户端。让我们来验证一下这种行为。将服务端的 app.config 文件中的 basicHttpBinding
替换为 wsHttpBinding
,并更新客户端文件。您的服务端的 app.config 文件中的新终结点应该如下所示
<endpoint address="pqr" binding="wsHttpBinding"
contract="wcfservice.Iservice" name="b"/>
更改配置后运行应用程序,并单击 HTTP 按钮两次。您会观察到,在两次服务调用期间,客户端和服务端都具有相同的会话 ID、实例上下文和服务实例。这些对象之所以保持持久,是因为 InstanceContextMode
的默认値是 PerSession
,而 SessionMode
的默认値是 Allowed
。因此,服务接受了有会话的绑定,并由有会话的服务对象提供服务。为了进一步实验,让我们用这些属性来装饰我们的代码。您的代码应该如下所示
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface Iservice
{
//some code…
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class serviceclass : Iservice
{
// some code…
}
在进行这些更改后运行代码并单击 HTTP 按钮,您将不会观察到与先前结果的任何变化。现在将实例上下文模式更改为 PerCall
,生成新的代理(通过 svcutil),然后更新客户端。您的代码应该如下所示
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class serviceclass : Iservice
{
// some code…
}
运行代码并单击 Http 按钮两次,您会注意到这一次总是会创建一个新的服务对象对来处理请求。即使您单击 Tcp 按钮,也会发生同样的情况。现在只需将 InstanceContextMode
设置为 single。您的代码应该如下所示
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class serviceclass : Iservice
{
// some code…
}
现在运行代码并单击两个按钮。这次您会看到 Tcp 和 Http 请求都由相同的服务对象处理。这是因为 InstanceContextMode.Single
将服务配置为由相同的服务对象处理所有请求。
我希望到目前为止介绍的概念对您来说已经很清楚了。有时,即使不运行代码,您也可以通过分析 SessionMode
和 InstanceContextMode
属性的不同配置値来自己推导出结果。我建议您进行更多随机配置并进行一些尝试,以便更清楚地理解这个概念。(请记住,默认情况下,InstanceContextMode
为 PerSession
,SessionMode
为 Allowed
。如果底层绑定不支持会话,那么即使服务是有会话的,它也会以 Per call 的方式响应。)
操作的界定
界定是通过一些特殊属性来标注服务操作,以确定其执行顺序中的第一个和最后一个操作。考虑一个服务,它有 4 个方法/操作,名称分别为 SignIn()
、GetDetails()
、TransferFund()
和 SignOut()
。在这种情况下,用户必须先登录,然后才能尝试获取详细信息和进行转账。如果用户退出登录,则在他们再次登录之前,不允许进行任何进一步的请求。为了配置这种执行顺序,需要进行界定。有两种属性
IsInitiating
(默认 -True
)IsTerminating
(默认 –False
)
这些属性决定了哪个操作应该先调用,哪个应该后调用?对于上面的四个操作,下面是一种可能的顺序
[OperationContract(IsInitiating = True)]
Bool SignIn()
[OperationContract(IsInitiating = false)]
String GetDetails()
[OperationContract(IsInitiating = false)]
Bool TransferFund()
[OperationContract(IsInitiating = false, IsTerminating = True)]
Bool SignOut()
这里的 initiation 和 termination 指的是会话,这对于界定来说是强制的,因为服务需要知道客户端是否遵循了特定的顺序。在这里,操作 2、3 和 4 被设置为 IsInitiating = false
,因此不能先调用它们,但在调用了 Isinitiating = True
的操作后可以调用它们。同样,操作 4 被标注为 IsTerminating = True
,因此当它被调用时,它会终止会话(以及底层的通道),然后客户端无法进行进一步的调用,直到创建一个新的代理并调用一个 IsInitiating = True
的操作。要使用界定,需要进行以下配置
- 支持会话的绑定
SessionMode
设置为Required
当调用 IsTerminating
操作时,WCF 会丢弃通道,并且不再接受来自它的进一步消息。如果某个操作没有明确用任何一个属性进行标注,那么这些属性的默认値将适用于它。
以上就是本期的全部内容。
最后,让我们回顾一下——WCF 会话有 3 件事情需要记住
- 支持会话的绑定
SessionMode
服务协定InstanceContextMode
服务行为
界定定义了执行顺序中的第一个和最后一个操作。
WCF 会话与 ASP.NET 会话在以下方面有所不同,因为在 WCF 中
- 会话由客户端创建和终止。
- 服务器不使用会话来存储通用数据。
- 会话不严重依赖于会话 ID,具有头部或正文中特定字段的一组消息也可以被视为会话的一部分。
- 会话 ID 在客户端和服务端可能不相同。
希望这篇简短的文章能让您对 WCF 会话有一个初步的了解,现在您可以为进一步阅读打下基础。请进行一些自己的实验,并参考 MSDN 深入分析这个概念。
如果有什么遗漏或需要更正的地方,请告诉我,这将对我们所有人都有帮助。
您可以参考我另一篇关于 **WCF 事务** 的文章 这里。
谢谢。