Learning WCF:Life Cycle of Service instance
示例代码下载地址:WCFDemo1Day
概述
客户端向WCF服务发出请求后,服务端会实例化一个Service对象(实现了契约接口的对象)用来处理请求,实例化Service对象以及维护其生命周期的方式在WCF中共有三种不同的类型,分别是:
- Per-Call
- Per-Session
- Single
程序中通过设置ServiceBehavior特性来指定,如下:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CommService : ICommContract
{
}
这是枚举InstanceContextMode的内容:
public enum InstanceContextMode
{
PerSession = 0,
PerCall = 1,
Single = 2,
}
Per-Call
每次调用服务端服务端方法,服务端都会实例化一个对象来处理请求,为观察结果,现编写如下代码:
服务类:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class CommService : ICommContract
{
public CommService()
{
Console.WriteLine("构造函数被执行");
}
public int Add(int a, int b)
{
Console.WriteLine("Add被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
return a + b;
}
public void Dispose()
{
Console.WriteLine("对象销毁");
Console.WriteLine("_____________________________");
}
public void SendStr(string str)
{
Console.WriteLine("SendStr被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
}
}
如果服务类实现了接口IDisposable
,当服务类被销毁时会回调Dispose
方法,可以在Dispose
方法中写一些WriteLine语句以观察对象的生命周期。
客户端代码:
class Program
{
static void Main(string[] args)
{
CommService.CommContractClient client = new CommService.CommContractClient();
client.SendStr("hello");
client.SendStr("hello");
client.SendStr("hello");
client.Close();
}
}
运行效果:
Per-Session
InstanceContextMode
被设置为PerSession
后,同一个客户端多次调用一个远程Web服务方法时,服务端只会实例化一次,修改上面的服务端代码第一行如下:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
再看运行效果:
上面的实例代码所使用的绑定模式为WSHttpBinding
,如果使用BasicHttpBinding
的话,即使是指定了InstanceContextMode
为PerSession
服务端也不会保存会话,现将支持Per-Session
的绑定方式列举如下:
WSXXXBinding(with Message security or reliability)
NetTcpXXXBinding
NetXXXPipeBinding
后两个容易理解,第一个括号里什么什么玩意儿,请看宿主代码:
class Program
{
static void Main(string[] args)
{
Uri baseURI = new Uri("http://localhost:8000/Services");
ServiceHost host = new ServiceHost(typeof(CommService), baseURI);
try
{
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(ICommContract), binding, "CommonService");
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = new Uri("http://localhost:8080/Services");
smb.HttpGetEnabled = true;
host.Description.Behaviors.Add(smb);
host.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
host.Close();
}
catch (CommunicationException ce)
{
Console.WriteLine("An exception occurred: {0}", ce.Message);
host.Abort();
}
}
}
binding.Security.Mode
默认为Message
,现在把它改成None
再运行程序:
哈,虽然InstanceContextMode
设置为了Per-Session
,实际上还是Per-Call
,这部分内容和安全性有关,希望我有时间以后会写到吧,今天暂时不去研究。
除了绑定方式,还有一个地方也影响到了Per-Session
是否起作用,它是契约接口特性ServiceContract
的属性SessionMode
,该枚举内容如下:
public enum SessionMode
{
Allowed = 0,
Required = 1,
NotAllowed = 2,
}
SessionMode
属性默认是Allowed
,如果设置为Required
表示必须启用会话模式,NotAllowed
表示必须不能启动会话模式,Allowed
表示无所谓。谁有兴趣可以一一尝试。现将契约接口的特性改成如下:
[ServiceContract(Namespace = "zzy0471.cnblogs.com.CommService", SessionMode = SessionMode.NotAllowed)]
public interface ICommContract : IDisposable
{
[OperationContract()]
int Add(int a, int b);
[OperationContract()]
void SendStr(String str);
}
再运行程序:
虽然InstanceContextMode
设置为了Per-Session
,实际上还是Per-Call
。
此外,还有一个需要注意的地方,如果在方法契约特性中设置属性IsTerminating
为true
,如下图
[OperationContract(IsTerminating = true)]
void SendStr(String str);
运行服务端会导致运行时错误,IsTerminating
设置为了true
和Per-Session
模式相矛盾,IsTerminating
的意思是:“获取或设置一个值,该值指示服务操作在发送答复消息(如果存在)后,是否会导致服务器关闭会话”
Per-Single
InstanceContextMode
被设置为PerSingle
后,所有的客户端请求都只有一个服务端实例对象来处理,服务启动时对象创建,服务关闭时对象销毁。现修改InstanceContextMode
特性如下:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class CommService : ICommContract
{
public CommService()
{
Console.WriteLine("构造函数被执行");
}
public int Add(int a, int b)
{
Console.WriteLine("Add被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
return a + b;
}
public void Dispose()
{
Console.WriteLine("对象销毁");
Console.WriteLine("_____________________________");
}
public void SendStr(string str)
{
Console.WriteLine("SendStr被调用,Thread:" + Thread.CurrentThread.ManagedThreadId + " fromPool:" + Thread.CurrentThread.IsThreadPoolThread);
}
}
为了方便观察,修改客户端代码,以模拟多个客户端:
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 3; i++)
{
CommService.CommContractClient client = new CommService.CommContractClient();
client.SendStr("hello");
client.SendStr("hello");
client.SendStr("hello");
client.Close();
}
}
}
运行程序:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)