初学者教程:理解 WCF 实例管理






4.83/5 (14投票s)
本文将讨论管理 WCF 服务类实例的各种方法。
引言
在本文中,我们将讨论管理 WCF 服务类实例的各种方法。我们将尝试了解所有可能的实例管理方法,并使用一个示例应用程序来逐一查看。我们还将简要讨论每种方法的优缺点以及何时可以使用每种技术。
背景
到目前为止,我们已经讨论了 WCF 服务的基础知识以及如何创建 WCF 服务(WCF 入门教程[^])。我们还探讨了托管 WCF 服务的各种方法(WCF 服务托管入门教程(IIS 托管和自托管)[^])以及如何处理 WCF 故障和异常(WCF 异常处理、故障异常和故障协定入门教程[^])。
现在,继续讨论。从 WCF 服务开发者的角度来看,了解我们的 WCF 服务类的实例是如何创建的以及它将如何影响整体服务操作非常重要。WCF 提供了根据实例创建模式配置服务行为的可能性,理解所有实例模式对于正确配置我们的 WCF 服务以正常工作非常重要。因此,让我们看看 WCF 服务的实例管理是如何工作的,以及我们如何根据需要进行配置。
配置 WCF 实例共有三种可能的方式。
-
每次调用
-
每个会话
-
Single
PerCall
模式表示此 WCF 服务类实例将在每次函数调用时创建。这些函数调用可能来自同一客户端或不同客户端。每当从客户端应用程序进行函数调用时,都会创建一个 WCF 服务类实例,调用该函数,然后处置该实例。现在,如果相同或任何其他客户端调用服务相同或任何其他方法,将创建一个新实例,并且无法回收旧实例。
PerSession
实例模式指定将为每个客户端创建一个 WCF 服务类实例。因此,如果客户端保持活动状态并对服务相同或多个方法进行多次调用,则将重用为此特定客户端创建的实例。当我们需要在服务上执行一系列操作并且需要在调用之间保留服务状态时,这非常有用。
Single
模式指定所有客户端的 WCF 服务类将只有一个实例。这更像是拥有一个所有客户端都使用的单例类。
注意:关于 PerSession
模式,有一个重要事项需要注意,它依赖于用于访问服务的协议。如果通过 http
(即 basicHttpBinding
)访问服务,则 PerSession
模式将不起作用,因为 http
本身在协议级别是无状态的,因此有状态的 PerSession
配置将不起作用。在这种情况下,为了获得有状态行为,服务类将不得不实现额外的逻辑来在方法调用之间保存和重新加载客户端的状态。
要配置实例模式,我们需要在 WCF 服务类实现中指定 ServiceBehavior
属性,并设置 InstanceContextMode
值。对于上述模式,该值分别为 InstanceContextMode.PerCall
、InstanceContextMode.PerSession
和 InstanceContextMode.Single
。
使用代码
现在,让我们使用示例应用程序来实际查看所有这些实例模式。让我们先实现一个简单的 WCF 服务,它将公开一个简单的 ServiceContract
。我们将使用此服务来测试实例模式。
准备测试服务
[ServiceContract]
public interface ISampleService
{
[OperationContract]
int TestFunction();
}
现在,此服务协定的实现将非常简单,我们将保留一个类成员变量来跟踪函数调用次数,并让调用者知道该函数在该实例上被调用了多少次。
public class SampleService : ISampleService
{
int count = 0;
public int TestFunction()
{
// Function called on this instance so let increment the instance variable
count += 1;
// Lets tell the called about the number of function calls he made
return count;
}
}
注意:我们目前尚未指定 InstanceContextMode
,但稍后会这样做。但如果我们不指定任何模式,则默认值为 PerSession
。
创建主机
我们将把此服务自托管在控制台应用程序中,并通过 TCP 公开服务。这样做的原因是我们只想在此示例中查看实例模式的行为,而不处理协议限制。(因为通过 http
托管将导致 PerSession
模式不起作用)。
因此,我们将服务的终结点设置为
- 地址:
net.tcp:///TestService
- 绑定:
netTcpBinding
- 协定:
SampleServiceNamespace.SampleService
托管服务的代码将如下所示
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(SampleServiceNamespace.SampleService)))
{
host.Open();
Console.WriteLine("Service up and running at:");
foreach (var ea in host.Description.Endpoints)
{
Console.WriteLine(ea.Address);
}
Console.ReadLine();
host.Close();
}
}
我们可以运行主机来访问我们的测试服务。然后,我们将更改 InstanceContextMode
的所有可能选项,并重新启动主机以测试新行为。目前运行主机将如下所示

注意:请参阅主机应用程序的 app.config
文件以查看完整配置。
创建测试客户端
现在,我们将创建一个简单的测试客户端,它将在循环中调用 WCF 服务函数 5 次。
static void Main(string[] args)
{
using (ServiceReference1.SampleServiceClient client = new ServiceReference1.SampleServiceClient())
{
for (int i = 1; i <= 5; ++i)
{
Console.WriteLine
(
string.Format
(
"Service called: {0} times, Return value: {1}",
i,
client.TestFunction()
)
);
}
}
Console.ReadLine();
}
现在我们已经准备好了所需的东西,让我们开始逐一测试所有实例模式。
测试 PerCall
我们要做的第一件事是将服务实现类的 InstanceContextMode
设置为 PerCall
。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class SampleService : ISampleService
{
int count = 0;
public int TestFunction()
{
// Function called on this instance so let increment the instance variable
count += 1;
// Lets tell the called about the number of function calls he made
return count;
}
}
现在,在启动服务主机应用程序后,我们将运行我们的客户端。输出将如下所示

从上面的输出可以清楚地看出,每次调用(即使来自同一客户端),都会创建一个新的 WCF 服务类实例,因此始终返回 1。
测试 PerSession
我们要做的第一件事是将服务实现类的 InstanceContextMode
设置为 PerSession
。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class SampleService : ISampleService
{
int count = 0;
public int TestFunction()
{
// Function called on this instance so let increment the instance variable
count += 1;
// Lets tell the called about the number of function calls he made
return count;
}
}
现在,在启动服务主机应用程序后,我们将运行客户端应用程序的多个实例。运行多个实例的原因是每个实例都将拥有自己的服务代理和独立的会话。输出将如下所示:

现在,从上面的输出可以清楚地看出,为每个客户端创建了一个 WCF 服务类实例。但是对于每个客户端,状态都被保留,并且在函数调用之间重用了同一个实例。
测试 Single
我们要做的第一件事是将服务实现类的 InstanceContextMode
设置为 Single
。
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class SampleService : ISampleService
{
int count = 0;
public int TestFunction()
{
// Function called on this instance so let increment the instance variable
count += 1;
// Lets tell the called about the number of function calls he made
return count;
}
}
现在,在启动服务主机应用程序后,我们将再次运行客户端应用程序的多个实例。运行多个实例的原因是每个实例都将拥有自己的服务代理和独立的会话。输出将如下所示

现在,当第一个客户端运行时,WCF 服务类实例被创建。所有后续来自同一客户端或任何其他客户端的请求都将从该实例提供服务。这就是为什么输出显示值不仅在函数调用之间,而且在多个客户端之间都被保留和递增的原因。
因此,我们看到了如何使用 PerCall
、PerSession
和 Single
模式配置 WCF 服务实例模式。当我们的服务使用一些有价值的资源(如连接对象、文件流)时,推荐使用 PerCall
。当我们的应用程序需要通过有状态协议访问服务并且需要在多个方法调用之间保留状态时,推荐使用 PerSession
。当只需要运行服务类的一个实例而不考虑客户端数量时,推荐使用 Single
。
关于 SessionMode 的说明
在服务协定中,我们还可以指定一个属性来配置 SessionMode
。此属性指定如何在客户端请求上创建会话。我们可以使用此属性来控制 WCF 服务的会话创建。
此属性有三个可能的值。
允许
不允许
必需
Allowed
选项指定此服务将接受来自有状态和无状态客户端的请求。会话不是必需的,但如果客户端希望以有状态方式工作,则允许。此模式是 WCF 服务的默认模式。
NotAllowed
选项指定服务将不以有状态模式工作,即它将仅接受来自无状态/状态无关客户端的请求。如果任何有状态/状态相关的客户端尝试发出请求,将抛出异常。
Required
选项声明服务将仅在有状态环境中工作,即只有有状态/状态相关的客户端可以请求服务操作。如果无状态客户端发出任何请求,将抛出异常。
这些模式很重要,因为 WCF 服务的最终行为将取决于在 ServiceBehavior
中为 InstanceContextMode
和在 ServiceContract
中为 SessionMode
选择的选项。因此,必须仔细设置这些选项,并牢记期望的行为。
注意:有两个更重要的相关概念与 Instancing 紧密相关,一个是有关如何在有状态服务中指定操作顺序,另一个是 WCF 服务的并发控制。有关这些主题的详细信息可以在以下文章中找到:
关注点
在本文中,我们了解了配置 WCF 服务类实例的各种方法。当我们需要有状态或单例服务行为时,InstanceContextMode
起着非常重要的作用。
如果我们希望在使用 PerCall
时具有状态,或者需要我们的服务在无状态协议/绑定上维护状态,那么服务将不得不实现自己的状态管理逻辑(可能像在每次方法调用时序列化和反序列化状态)。此外,在有状态协议上,如果我们需要为服务使用 PerSession
模式,我们还需要注意操作顺序。这两个主题都需要单独讨论,在本篇文章中未包含,以避免跑题。
本文是从初学者的角度编写的,但读者至少应该了解 WCF 服务和 WCF 托管的基础知识才能完全理解本文。希望本文具有启发性。
历史
- 2013 年 3 月 23 日:初稿。