实例是指客户端在调用服务接口类的方法时,服务端需要将这个类先实例化出一个对象,然后在这个对象上调用方法,将结果返回给客户端。
服务端实例化的方式有三种,分别是"每调用实例","每会话实例"和"单一实例"。实例化模式的指定是通过配置服务类的ServiceBehavior属性中的InstanceContextMode属性来实现的。注意,是服务类的属性,而不是服务协定。
(1)每调用实例
将服务类的ServiceBehavior属性中的InstanceContextMode属性设置为PerCall来启用这种模式。
在这种实例化模式下,客户端对服务方法的每一次调用,服务端都会new一个实例,方法调用结束即销毁,这种模式可能要频繁的花费创建对象和销毁对象的花销,因此性能比较差,但是每次调用后服务对象被销毁,对象所把持的资源也就被释放(如文件啦、数据库连接啦),因此伸缩性是最好的。
只需要在服务类的属性上添加属性:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
具体代码如下:
服务端svc文件:
using System;
using System.ServiceModel;
namespace LearnWCF
{
[ServiceContract(SessionMode=SessionMode.Required) ]
public interface IHelloWCF
{
[OperationContract]
string PHelloWCF();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class HelloWCF:IHelloWCF
{
private int _Counter;
public string PHelloWCF()
{
_Counter++;
return "Hello!You called " + _Counter.ToString() + " times";
}
}
}
服务端 cs文件:
using System;
using System.ServiceModel;
namespace LearnWCF
{
[ServiceContract(SessionMode=SessionMode.Required) ]
public interface IHelloWCF
{
[OperationContract]
string PHelloWCF();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class HelloWCF:IHelloWCF
{
private int _Counter;
public string PHelloWCF()
{
_Counter++;
return "Hello!You called " + _Counter.ToString() + " times";
}
}
}
客户端调用代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SessionMode_client
{
class Program
{
static void Main(string[] args)
{
SessionMode_client.SessionMode.HelloWCFClient client = new SessionMode.HelloWCFClient();
Console.WriteLine(client.PHelloWCF());
Console.WriteLine(client.PHelloWCF());
client.Close();
Console.ReadLine();
}
}
}
运行结果如下:
从结果我们可以看出,虽然SessionMode设置成了Require(必须使用对话),即这个时候服务端和客户端是在进行会话连接的时候,服务端还是为两次客户端调用分别实例化了对象,所以看到计数没有增长,每次都是1。
(2)每会话实例
将服务类的ServiceBehavior属性的InstanceContextMode属性设置为PerSession时启用这种模式。(如果不指定,那么这个值是默认的配置)
这种模式下服务端在受到会话的第一个调用时实例化对象,直到会话关闭才销毁对象,这样减少了对象的建立和销毁开销,性能有一定提升,但是在会话期间申请的资源如文件和数据库连接直到会话结束才会释放,伸缩性有所下降了。
实现代码只需将属性修改即可,即:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
保存运行结果如下:
该结果与上篇中未设置实例化属性一致。
延伸:如果将SessionMode属性修改成NotAllowed,那么运行每会话实例的话,结果如何?结果应该与PerCall一致,因为PerSession模式需要会话的支持,如果协定不支持会话,服务端就不可能记得住客户端了,也可以理解成每次调用都是一个全新的会话了。
(3)单一实例
把服务类的ServiceBehavior属性的InstanceContextMode属性设置为Single来启用这种模式。
在这种模式下,服务类在受到首次调用的时候实例化,然后一直不销毁,直到服务宿主关闭,在这期间无论是会话连接还是非会话连接,服务端都用这个实例进行调用。这样实际上几乎没有创建和销毁对象的开销,但是如果一旦实例把持资源,则一直都不能释放,毫无伸缩性可言。
实现代码只需将属性修改即可,即:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
保存运行一次、二次、三次的结果如下:
从结果中我们可以看到,计数器持续增长,就算客户端程序已经重新运行,并且很显然是个新的会话,但是服务端的实例一直没有销毁,所以计数器就继续增长了。在这种模式下,无论我们把协定的SessionMode改成什么值,结果都是一样的,因为服务端已经认准了一个实例,无论客户端连接是不是会话了。
总结:
会话模式和实例化模式之间互相的影响:
(1) 如果协定不支持会话,那么PerSession相当于PerCall。
(2) 如果服务类为PerCall,那么协定的是否支持会话结果相同。
(3) 如果服务类为Single,那么协定是否支持会话结果相同。