一、实例上下文模式概述

实例上下文(IntanceContext Mode)表示服务端的服务实例与客户端的服务代理的绑定方式。

在实例化服务器对象时,WCF采用了3种不同的模式:单调(Per-Call)模式,会话(Per-Session)模式和单例(Single)模式.其中会话模式是默认的。

服务器实例化模式的选择只在服务端是可见的,并没有反映到WSDL文档中。由于每当客户端调用一个方法时,它并不知道接受对象是否来自同一个实例,也不知道以前设置的值是否保留了下来,更不知道每次调用的实例是否否是重新创建的,因此,在设计时必须要特别小心。

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class HelloWorldService : IHelloWorldService
{
}

二、单调(Per-Call)模式

对于单调的实例上下文模式,WCF服务端运行时总是创建一个全新的InstanceContext来处理每一个请求,不管该请求是否来自相同的客户端。所以在单调实例上下文模式下,根本就不存在对某个InstanceContext的并发调用的情况发生。

每当客户端发出请求(即WCF契约的方法调用),就会获得一个新的专门的服务实例。

以下列出了单调模式的执行细节:

(1)客户端调用代理,代理将调用转发给服务。

(2)WCF创建一个服务实例,然后调用服务实例的方法。

(3)当方法调用返回时,如果对象实现了IDisposable接口,则WCF将自动调用IDisposable.Dispose()方法。WCF随后销毁上下文。

(4)客户端调用代理,代理将调用转发给服务。

(5)WCF创建一个服务实例,然后调用服务实例的方法。

需要关注的是服务实例的销毁。如果服务实现了IDisposable接口,WCF会自动调用Dispose()方法,允许服务执行所有必要的清除工作。注意,调用Dispose()方法的现场与分发原有方法调用的线程是相同的,同时,Dispose()还包含了一个操作上下文。一旦调用了Dispose()方法,WCF就会断开实例与其它WCF基础架构的连接,然后将它放到垃圾回收器中等待回收。

单调模式的优势

(1) 能够最大限度的发挥资源的利用,避免了资源的闲置及相互争用

(2) 如果每次调用都需要事务资源和事务编程,那么单调服务会强行要求服务实例重新分配或连接资源,从而减轻同步实例状态的任务。

(3) 它可以用于队列离线调用,因为它能够建立服务实例与离散队列消息之间的简单映射。

使用单调模式

////////////////////服务端代码////////////////////
[ServiceContract(Namespace = "www.cnblogs.com")]
public interface IService1
{
[OperationContract]
void MyMethod();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class Service1:IService1,IDisposable
{
int m_Counter = 0;
public Service1()
{
Debug.WriteLine("Service1.Service1()");
}
public void MyMethod()
{
m_Counter++;
Debug.WriteLine("Counter=" + m_Counter);
} public void Dispose()
{
Debug.WriteLine("Service1.Dispose()");
}
}

////////////////////客户端代码////////////////////

Service1Client client = new Service1Client();

client.MyMethod();

client.MyMethod();

client.Close();

////////////////////输出////////////////////

Service1.Service1()

Counter=1

Service1.Dispose()

Service1.Service1()

Counter=1

Service1.Dispose()

设计单调模式

虽然理论上可以将单调实例激活模式应用到任意一个服务类型上,但实际上,需要在一开始就将服务以及服务的契约设置为支持这一模式。主要问题是客户端并不知道是否需要发出调用时获取一个新的实例。单调服务必须是状态相关的,也就是说,它必须积极的管理自己的状态,并在持续的会话过程中给出客户端一个提示,一个状态相关的服务与状态无关的服务迥然不同。事实上,正是这种状态相关,才需要使用单调模式。单调服务的一个实例创建于每个方法调用之前,调用完成后会立即销毁该服务实例。因此,在每次调用之初就要获取存储在某处的值用来初始化它的状态。

使用单调模式,对于操作的设计而言是一个重要的启示,那就是每个操作都必须定义一个参数,用来识别哪些是需要重新获取状态的服务实例。因此,状告太存储通常都具有键值,如订单号,银行帐号参数等。

如下实例的一个单调模式范例:

[ServiceContract(Namespace = "www.cnblogs.com")]
public interface IService1
{
[OperationContract]
void MyMethod();
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class Service1:IService1,IDisposable
{
public void MyMethod()
{
GetState(state);
DoWork();
SaveState(state)
} void GetState(Param state)
{
....
}
void DoWork()
{
....
}
void SaveState(Param state)
{
....
}
public void Dispose()
{
Trace.WriteLine("Service1.Dispose()");
}
}</pre>

单调服务会为每次方法调用获取与保存实例状态,这势必影响系统的性能,但换来了可伸缩性,这是因为它持有了服务的所以来的状态与资源。

选择单调服务

在客户端/服务端开发者的眼中,单调服务的编程模型似乎有点违背常理,但对于大多数服务而言,单调服务才是最好的实例管理模式。这仅仅是因为单调服务更有利于系统的伸缩性,至少它可以保证规模不变。

三、会话(Per-Session)模式

会话模式表示,每个客户端代理都与服务端的一个专用实例进行通信。只要客户端不执行代理的Close方法,或者会话的超时时间还没到(默认10分钟),此对象在服务器端就会一直持续下去。

当通过调用Close方法关闭此客户端到服务器的连接后忙超时已删除了PerSession服务对象之后,代理就不可以继续使用了。如果试图再次调用一个方法,就会产生一个通信异常错误。

因为在整个会话期间,服务实例都保留了内存空间,并用它来维持会话的状态,这就是的这种编程模型更近似于经典的客户端/服务端模式。因此,它与客户端/服务端模式一样,仍然存在可伸缩性及事务处理的问题。一个配置了私有会话的服务通常无法支持多大几十个(或者上百个)独立的客户端,只因为创建专门的服务实例的代价太大。

配置私有会话

 [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class Service2:IService2
{...}

InstanceContextMode的默认值为InstanceContextMode.PerSession,那么以下是等效的:

    public class Service2:IService2,IDisposable
{...}
[ServiceBehavior]
public class Service2:IService2,IDisposable
{...}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class Service2:IService2,IDisposable
{...}

为了关联所有从特定客户端发送到特定实例的消息,WCF需要具有识别客户端的能力。ServiceContract特性提供了SessionMode枚举类型,即

    public enum SessionMode
{
// 指定当传入绑定支持会话时,协定也支持会话。
Allowed = 0,
// 指定协定需要会话绑定。 如果绑定并未配置为支持会话,则将引发异常。
Required = 1,
// 指定协定永不支持启动会话的绑定。
NotAllowed = 2,
}

SessionMode的默认值为SessionMode.Allowed。当客户端导入契约元数据时,服务元数据将包含SessionMode的值,并会如是反映它的内容。

SessionMode.Allowed值

SessionMode.Allowed是SessionMode的默认值。所以,以下是等效的。

    public interface IService2
{...}
[ServiceContract(SessionMode=SessionMode.Allowed)]
public interface IService2
{...}

所有的绑定都允许将终结点上的契约配置为SessionMode.Allowed。当SeesionMode互相被配置为该值时,才允许传输会话,但不是强制。BasicHttpBinding绑定由于HTTP协议本质是无连接的,因此它永远不可能拥有传输层会话,即使服务配置为InstanceContextMode.PerSession,并且契约的SessionMode值为SessionMode.Allowed,服务仍然会采用单调服务的执行方式。

SessionMode.Required值

SessionMode.Required值要求必须使用传输层会话,但对应用层会话却不是必须的。然而,我们仍然可以将服务配置为单调服务,服务实例会在每次客户端调用期间创建与销毁实例。只有当服务被配置为会话服务时,服务实例才会存活于整个客户端会话中。如:

    [ServiceContract(SessionMode=SessionMode.Required)]
public interface IService2
{...}
public class Service2:IService2,IDisposable
{...}</pre>

若要设计一个会话契约,主张使用SessionMode.Required值,而非SessionMode.Allowed值

/////////////////////服务端代码/////////////////////
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService2
{
[OperationContract]
void MyMethod();
}
public class Service2:IService2,IDisposable
{
int m_Counter = 0;
public Service2()
{
Trace.WriteLine("Service2.Service1()");
}
public void MyMethod()
{
m_Counter++;
Debug.WriteLine("Counter=" + m_Counter);
} public void Dispose()
{
Debug.WriteLine("Service2.Dispose()");
}
}

/////////////////////服务端代码/////////////////////

Service2Client client = new Service2Client();

client.MyMethod();

client.MyMethod();

client.Close();

/////////////////////输出/////////////////////

Service2.Service1()

1

2

Service2.Dispose()

SessionMode.NotAllowed值

SessionMode.NotAllowed值用于禁止使用传输层会话,也不允许使用应用层会话。不管如何配置,使用该值的服务总是采用单调服务方式。

考虑到代码的可读性,建议在选择使用SessionMode.NotAllowed值的同时,总是将服务配置为单调服务,即:

 [ServiceContract(SessionMode=SessionMode.NotAllowed)]
public interface IService2
{...}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class Service2:IService2
{...}</pre>

绑定、契约配置和服务行为与它们对应的实例模式

四、单例(Single)模式

Single模式下,不管有多少个客户端代理,只有一个实例,服务对象只实例化一次,服务的生命周期有ServiceHost来决定。

Single模式是一种极端的共享式服务。当一个服务被配置为单例时,所有客户端都将独自连接相同的单个实例,而不考虑它们连接的是服务的哪一个终结点。单例服务会在创建宿主时创建,且它的生存期是无限的:只有关闭宿主时,单例服务才会被释放。

使用单例服务不需要客户端维护单例实例的逻辑会话,也不需要使用支持传输层会话的绑定。如果客户端调用的契约拥有一个会话,那么在调用期间,单例服务将与拥有一个与客户端相同的会话ID。关闭客户端代理只能终止传送会话,不能终止单例实例,单例服务的会话永远不会过期。

如下代码,单例服务同时实现了两个契约,其中一个契约需要会话,另一个不需要会话。从客户端的调用可以看出,在两个终结点上的调用都经有相同的实例进行传递,即使关闭代理,仍然不会终止单例服务。

 //////////////////////服务端代码//////////////////////
[ServiceContract(SessionMode=SessionMode.Required)]
public interface IService3
{
[OperationContract]
void MyMethod();
}
[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IOtherService3
{
[OperationContract]
void MyOtherMethod();
} public class Service3:IService3,IOtherService3,IDisposable
{
int m_Counter = 0;
public Service3()
{
Trace.WriteLine("Service3.Service3()");
}
public void MyMethod()
{
m_Counter++;
Trace.WriteLine("Counter=" + m_Counter);
} public void MyOtherMethod()
{
m_Counter++;
Trace.WriteLine("Counter=" + m_Counter);
} public void Dispose()
{
Trace.WriteLine("Service1.Dispose()");
}
}

//////////////////////客户端代码//////////////////////

Service3Client client = new Service3Client();

client.MyMethod();

client.Close();

OtherService3Client client1 = new OtherService3Client();

client1.MyMethod();

client1.Close();

//////////////////////输出//////////////////////

Service3.Service3()

Counter=1

Counter=2

 

初始化单例服务

有时,我们无法使用默认的构造函数创建和初始化单例服务,ServiceHost类提供了专门的构造函数,以接收一个object对象。

public class ServiceHost : ServiceHostBase
{
public ServiceHost(object singletonInstance, params Uri[] baseAddresses);
public object SingletonInstance { get; }
//...
}

注意,object参数对象必须配置为单例方式。

//服务端
[ServiceContract]
public interface IService4
{
[OperationContract]
void MyMethod();
}
public class Service4:IService3
{
public int Counter { get; set; }
public void MyMethod()
{
Counter++;
Trace.WriteLine("Counter=" + Counter);
}
}

//宿主

Service.Service4 singleton = new Service.Service4();

singleton.Counter = 287;

ServiceHost host = new ServiceHost(singleton);

host.Open();

//客户端

Service4Client client = new Service4Client();

client.MyMethod();

client.Close();

//输出

Counter=287

选择单例模式

只有应用程序域符合单例模式场景才能使用单例对象。一个纯粹的单例对象一种性质单一且唯一的资源。典型的例子就是 全局日志。

[WCF编程]7.实例上下文模式的更多相关文章

  1. WCF实例上下文模式与并发模式对性能的影响

    实例上下文模式 InstanceContextMode 控制在响应客户端调用时,如何分配服务实例.InstanceContextMode 可以设置为以下值: •Single – 为所有客户端调用分配一 ...

  2. WCF实例上下文

    实例上下文模式(IntanceContext Mode)表示服务端的服务实例与客户端的服务代理的绑定方式. 在WCF中有三种不同的实例上下文模式,单调(Per-Call)模式,会话(Per-Sessi ...

  3. WCF - 服务实例管理模式

    WCF 提供了三种实例上下文模式:PreCall.PreSession 以及 Single.开发人员通过 ServiceBehavior.InstanceContextMode 就可以很容易地控制服务 ...

  4. [WCF编程]9.性能与限流

    一.性能概述 WCF服务的性能取决于很多因素.出了CPU.RAM和网络性能等常见的因素外,实例上下文模式.并发模式.数据契约的设计或使用的绑定等与WCF有关的因素都起着重要的作用. 实例上下文模式用来 ...

  5. [WCF编程]8.服务实例的生命周期

    一.服务实例的生命周期概览 我们已经直到,通过显式调用Close方法或等待默认的超时时间到来,都可以释放服务实例.但是,在会话连接里,经常需要按一定顺序调用方法. 二.分步操作 会话契约的操作有时隐含 ...

  6. 《C#并发编程经典实例》学习笔记—2.7 避免上下文延续

    避免上下文延续 在默认情况下,一个 async 方法在被 await 调用后恢复运行时,会在原来的上下文中运行. 为了避免在上下文中恢复运行,可让 await 调用 ConfigureAwait 方法 ...

  7. [WCF编程]10.操作:回调操作

    一.回调操作概述 WCF支持服务将调用返回给它的客户端.在回调期间,许多方面都将颠倒过来:服务将成为客户端,客户端将编程服务.回调操作可以用在各种场景和应用程序中,但在涉及事件或者服务发生时间需要通知 ...

  8. .Net-WCF-图书:《WCF编程》

    ylbtech-.Net-WCF-图书:<WCF编程> <WCF编程>是2008年1月机械工业出版社出版的图书,作者是Juval Lowy.Clemens Vasters. 1 ...

  9. 《C#并发编程经典实例》笔记

    1.前言 2.开宗明义 3.开发原则和要点 (1)并发编程概述 (2)异步编程基础 (3)并行开发的基础 (4)测试技巧 (5)集合 (6)函数式OOP (7)同步 1.前言 最近趁着项目的一段平稳期 ...

随机推荐

  1. (翻译)《Hands-on Node.js》—— Why?

    事出有因 为何选择event loop? Event Loop是一种推进无阻塞I/O(网络.文件或跨进程通讯)的软件模式.传统的阻塞编程也是用一样的方式,通过function来调用I/O.但进程会在该 ...

  2. Lesson 13 The Greenwood Boys

    Text The Greenwood Boys are group of pop singers. At present, they are visiting all parts of the cou ...

  3. clojure基础入门(一)

    最近在看storm的源码,就学习分享下clojure语法. 阅读目录: 概述 变量 运算符 流程控制 总结 概述 clojure是一种运行在JVM上的Lisp方言,属于函数式编程范式,它和java可以 ...

  4. DevExpress GridControl使用方法

    一.如何解决单击记录整行选中的问题 View->OptionsBehavior->EditorShowMode 设置为:Click 二.如何新增一条记录 (1).gridView.AddN ...

  5. [备忘]没有为扩展名“.cshtml”注册的生成提供程序

    webconfig中配置 <compilation debug="true" targetFramework="4.5.1">       < ...

  6. C语言 · 2的次幂表示

    问题描述 任何一个正整数都可以用2进制表示,例如:137的2进制表示为10001001. 将这种2进制表示写成2的次幂的和的形式,令次幂高的排在前面,可得到如下表达式:137=2^7+2^3+2^0 ...

  7. CSS系列:CSS文字样式

    1. 设置字体 在CSS中字体通过font-family属性来设置. font-family: Verdana, Arial, Helvetica, sans-serif; 上面的字体设置告诉浏览器首 ...

  8. 【转】WPF DataGrid 获取选中的当前行某列值

    方法一:DataRowView mySelectedElement = (DataRowView)dataGrid1.SelectedItem; string result = mySelectedE ...

  9. 新作《ASP.NET MVC 5框架揭秘》正式出版

    ASP.NET MVC是一个建立在ASP.NET平台上基于MVC模式的Web开发框架,它提供了一种与Web Form完全不同的开发方式.ASP.NET Web Form借鉴了Windows Form基 ...

  10. 浅谈async、await关键字 => 深谈async、await关键字

    前言 之前写过有关异步的文章,对这方面一直比较弱,感觉还是不太理解,于是会花点时间去好好学习这一块,我们由浅入深,文中若有叙述不稳妥之处,还请批评指正. 话题 (1)是不是将方法用async关键字标识 ...