准备技术:
WCF 服务契约、数据契约等基础知识
Donet 基本开发
内容概要:
三种实例激活类型
单调服务
会话服务
单例服务
实例类型
WCF 支持三种实例类型: PerCall 、 PerSession 、 Single 。 PerCall 就是单调服务会为每次客户端的请求去分配一个新的服务实例; PerSession 会为每次客户端连接分配一个实例; Single 所有的客户端会去共享一个相同的服务实例。
WCF 是通过 ServiceBehavior 特性中的 InstanceContextMode 属性来告诉服务实例采用那种类型, InstanceContextMode 属性是一个 InstanceContextMode 的枚举类型, InstanceContextMode 有三个成员: PerCall (单调) ,PerSession (会话) ,Single (单例)。
单调服务
单调服务实例只存在于客户端的调用过程中,每次客户端的一个请求就会获得一个新的服务实例。也就说我们在客户端的每次调用一个方法时都会去重新返回一个新的实例给我们。下面我们看一个配置为单调服务实例的例子。
首先看我们的服务契约很简单 IBehaverContract.cs :
Code
[ServiceContract(Name = " IBehaverContract " )]
public interface IBehaverContract
{
[OperationContract(Name = " TestBehavor " )]
string TestBehavor();
}
我们的服务实例BehaverContract.cs为:
Code
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCal)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
{
private int _Count = 0 ;
public BehaverContract()
{
Console.Write( " BehaverContract.BehaverContract()\n " );
}
public string TestBehavor()
{
_Count ++ ;
return String.Format( " The number is :{0} " , _Count);
}
public void Dispose()
{
Console.Write( " BehaverContract.Dispose()\n " );
}
}
我们在客户端调用:
Code
string strResult = proxy.TestBehavor();
string strResult2 = proxy.TestBehavor();
Console.Write(strResult + " \n " );
Console.Write(strResult2);
结果如图:
同时我们在宿主端可以监听到:
通过实例可以清楚的看到我们调用了两次方法,每次都是创建了一个新的实例给客户端,也就是说单调实例不能在客户端跟服务器端交互的时候维持某些状态。这样的模式的好处就是可以很好的去使用跟释放我们比较有限的资源,如数据连接。单调服务创建于每个方法调用之前,调用完成后会立刻释放。虽然说服务端在不断的创建跟销毁服务实例,但是服务端与客户端的代理的连接是不会断开的,这样每次的创建的资源就比较少了。
会话服务
如果服务被配置为会话服务类型,那么客户端为该服务创建一个新的代理时,就会获得一个新的而且是自己专有的,跟其他实例无关的服务实例。这种模式非常像经典的 c/s模式,该实例一直到客户端不在需要使用时才会去释放。 PerSession模式也就是相当于 remoting中的客户端激活模式。
在配置私有会话时候,我们需要在服务上配置 InstanceContextMode为 PerSession, PerSession也是 InstanceContextMode属性的默认值。配置了这样的属性时,并不能确保我们的服务就是私有会话的,因为私有会话模式需要维持某一个客户端到服务器端的状态,所以需要客户端到服务端的连接是持久的。那么 Http协议的无连接就不能维持私有会话了,如果要基于 Http的传输 WCF提供了 wsHttpBinding来模仿一个持久的传输。
在传输层的会话的同时,契约的配置也是需要的,因为在运行时 WCF需要知道服务是否启用了会话。通过 ServiceContract的 SessionMode属性,我们可以来设定我们的契约是否支持会话。
SessionMode枚举有三个成员: Allowed、 Required、 NotAllowed。 SessionMode属性的默认值为 SessionMode. Allowed。当我们设置为 Allowed时只能代表是允许,只有当绑定是一个传输层会话时才能采用会话服务的执行。 SessionMode.Required要求必须使用传输层会话。 SessionMode.NotAllowed禁止使用传输层会话,也就不能使用应用层会话了,也就是说即使我们将 InstanceContextMode属性配置为 InstanceContextMode.PerSession时,它总会采用 PerCall去执行。
我们还看刚才那个的那个例子,只是把相应的配置给修改下。首先我们将服务契约的 SessionMode设置为 SessionMode.Requried :
Code
[ServiceContract(Name = " IBehaverContract " ,SessionMode = SessionMode.Required)]
public interface IBehaverContract
然后我们将服务实例设置为: InstanceContextMode.PerSession :
Code
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
同时我们需要在宿主中的绑定配置为支持传输层会话的:
Code
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< configuration >
< system.serviceModel >
< services >
< service name = " Service.BehaverContract " behaviorConfiguration = " behavior " >
< endpoint binding = " wsHttpBinding "
contract= " ServiceContracts.IBehaverContract "
address = " BehaverContract "
bindingConfiguration= " HttpSession " >
</ endpoint >
< host >
< baseAddresses >
< add baseAddress = " http://localhost:8000 " />
</ baseAddresses >
</ host >
</ service >
</ services >
< behaviors >
< serviceBehaviors >
< behavior name = " behavior " >
< serviceMetadata httpGetEnabled = " true " />
</ behavior >
</ serviceBehaviors >
</ behaviors >
< bindings >
< wsHttpBinding >
< binding name = " HttpSession " >
< reliableSession enabled = " true " />
</ binding >
</ wsHttpBinding >
</ bindings >
</ system.serviceModel >
</ configuration >
客户端的调用还是一样的,我们看到如下的结果:
在我们启用了私有会话的时候,服务端跟客户端都能获得此次会话的一个唯一的会话 ID 。服务可以在操作上下文中去获取这个 ID(OperationContext.Current.SessionId), 客户端可以通过 InnerChannel.SessionId 去获取。服务端跟每个客户端的会话维持就是通过这个 SessionId 去关联的。
会话一般是在客户端关闭了代理时才会结束,但是客户端可以强行的终止会话。每次会话空闲超时时间为 10 分钟。我们可以通过绑定配置 ReliableSession 的 InactivityTimeout 属性去设置超时时间。
单例服务
单例服务就是说所有的客户端都将连接相同的实例,单例服务的实例生命周期是无限的。配置时,我们只需要在将 InstanceContextMode 属性设置为 InstanceContextMode.Single 既可以。还看刚才的例子,我们把服务修改成:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Service
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class BehaverContract : ServiceContracts.IBehaverContract,IDisposable
{
private int _Count = 0 ;
public BehaverContract()
{
Console.Write( " BehaverContract.BehaverContract()\n " );
}
public string TestBehavor()
{
_Count ++ ;
return String.Format( " The number is :{0} " , _Count);
}
public void Dispose()
{
Console.Write( " BehaverContract.Dispose()\n " );
}
}
}
客户端:
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Client
{
class Program
{
static void Main( string [] args)
{
BehaverContract.BehaverContractClient proxy = new Client.BehaverContract.BehaverContractClient();
proxy.Open();
string strResult = proxy.TestBehavor();
BehaverContract.BehaverContractClient proxy2 = new Client.BehaverContract.BehaverContractClient();
string strResult2 = proxy2.TestBehavor();
Console.Write(strResult + " \n " );
Console.Write(strResult2);
// Console.Write("\nSession ID is {0}", proxy.InnerChannel.SessionId);
proxy.Close();
Console.Read();
}
}
}
运行结果:
服务器端:
在客户端我们是用了两个代理,通过服务器端的监听以及运行的结果,我们可以清楚的看到单例模型实现的方式。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?