单例模式效果可以用下面这张图表示,服务端的服务实例只有一个,任何一个客户端访问的服务端都是相同的服务实例。意味着服务端可以留下不同客户端的脚印。
使用也很简单,只需要将ServiceBehavior的上下文模式InstanceContextMode设置为Single即可。可以参照上一篇介绍实例上下文模式:单调模式
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class Calculator : ICalculator,IDisposable
{
......
}
上篇介绍实例上下文模式时有些概念没有讲清楚。这里来补一下。
ServiceHost有两个有参数的构造函数,分别是
public ServiceHost(object singletonInstance, params Uri[] baseAddresses);
public ServiceHost(Type serviceType, params Uri[] baseAddresses);
第一个构造函数,singletonInstance是指服务实例,即new Calculator()创建的实例对象。
第二个构造函数是指服务实例的类型,即typeof(Calculator)获得。
baseAddresses均指终结点的基地址,这里需要注意,每一种Binding类型只能有一个基地址。例如下面寄宿代码将会报错,因为baseAddrs中含有两个http的基地址。
回到正题,通过观察ServiceHost的构造函数代码。会将服务实例放入singletoninstance,类型放入serviceType。 这两个字段在ServiceHost中都是私有的。Servciehost启动的时候先检查singletonInstance是否存在,若不存在则根据serviceType反射创建一个服务实例。
了解单例的机制需要知道一下几点:
1.当服务启动时候会调用服务行为(ServiceBehavior)初始化ServiceHost。而服务行为实现了IServiceBehavior接口,它定义了一个ApplyDispatchBehavior接口。
public interface IServiceBehavior
{
void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters);
void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase);
}
此接口的作用是将创建的实例上下文(InstanceContext)附加到终点分发器(EndpointDispatcher)的DispatchRuntime的SingletonInstance上。
2.实例上下文提供者实现了接口IInstanceContextProvider,实例上下文提供者附加在终点分发器(EndpointDispatcher)的DispatchRuntime的InstanceContextProvider上。
public interface IInstanceContextProvider
{
InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel);
void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel);
bool IsIdle(InstanceContext instanceContext);
void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);
}
当服务端接受到一个请求消息时,根据消息地址报头和Action选好终结点分发器(EndpointDispatcher)后,首先会调用终结点分发器中的运行时(DispatchRuntime)获取服务上下文提供者(IInstanceContextProvider),再调用服务上下文提供者的GetExistingInstanceContext获取InstanceContext,再从上下文中获取服务实例,调用实例的方法。流程处理完毕后调用IsIdle方法,如果返回为true则标识当前实例上下文生命周期结束,GC回收。
上一篇介绍单调时,直接将GetExistingInstanceContext返回null,WCF会创建一个新的实例上下文,使用完毕后再调用Isdle,直接返回true,则让GC回收。因此就有了单调模型。
实例模型只需要保证在GetExistingInstanceContext时返回一个相同的实例上下文,并且不能被GC回收,因此IsIdle返回false即可。而这个相同的实例上下文,需要到终结点分发器中取出来。
下面自定义一个SingleServiceProvider,如前面所述,IsIdle返回false,确保不让GC回收。GetExistingInstanceContext返回内部的runtime中的SingletonInstanceContext。而这个runtime是在构造函数中指定。
public class SingleServiceProvider : IInstanceContextProvider
{
private DispatchRuntime runtime;
public SingleServiceProvider(DispatchRuntime runtime)
{
this.runtime = runtime;
}
public InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel)
{
return runtime.SingletonInstanceContext;
}
public void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel)
{}
public bool IsIdle(InstanceContext instanceContext)
{
return false;
}
public void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext)
{}
}
自定义SingleAttribute,遍历终结点分发器,若终ServiceHost中的服务实例SingletonInstance存在,则直接获取用来构造InstanceContext;若不存在则用serviceType反射形式创建一个服务实例,再构造InstanceContext。
public class SingleAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channel in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpoint in channel.Endpoints)
{
DispatchRuntime runtime = endpoint.DispatchRuntime;
ServiceHost host = (ServiceHost)serviceHostBase;
object Instance = null;
if (null != host.SingletonInstance)
{
runtime.SingletonInstanceContext = new InstanceContext(serviceHostBase, host.SingletonInstance);
Instance = host.SingletonInstance;
}
else
{
Instance = Activator.CreateInstance(serviceDescription.ServiceType);
runtime.SingletonInstanceContext = new InstanceContext(serviceHostBase, Instance);
}
endpoint.DispatchRuntime.InstanceContextProvider = new SingleServiceProvider(runtime);
if (serviceDescription.ServiceType.GetInterfaces().Contains(typeof(IDisposable)))
{
FieldInfo field = typeof(ServiceHost).GetField("disposableInstance", BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(host, Instance);
}
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{}
}
当ServiceHost结束时会调用onClosed方法,里面会释放private的disposableInstance字段。所以这里也将创建的服务实例通过反射放到这里,这样ServiceHost关闭时也能让服务实例也能回收。
最后使用自定义的SingleAttribute,可以看到同样实现了单例效果,并没有针对每次调用都创建一个实例上下文。
[Single]
public class Calculator : ICalculator,IDisposable
{
......
}