一、实例上下文模式概述

实例上下文(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. ABP理论学习之依赖注入

    返回总目录 本篇目录 什么是依赖注入 传统方式产生的问题 解决办法 依赖注入框架 ABP中的依赖注入基础设施 注册 解析 其他 ASP.NET MVC和ASP.NET Web API集成 最后提示 什 ...

  2. 初识WEB:输入URL之后的故事

    1. 概述2. HTTP请求过程3. 相关性能检测及优化手段4. 浏览器的呈现过程5. 浏览器的呈现引擎6. 引用及延伸阅读 概述 为什么输入www.cnblogs.com之后敲一个回车,浏览器就会显 ...

  3. GIT常用命令笔记

    最近在做了一个自己的项目.两个人合作的,所以需要用到版本管理工具.本来打算学一下自己搭建svn的,后来朋友推荐我用git,免费,流行,好用,逼格.所以就学习了一下.发现这个git与已经使用惯了的svn ...

  4. ENode框架Conference案例分析系列之 - Quick Start

    前言 前一篇文章介绍了Conference案例的架构设计,本篇文章开始介绍Conference案例的代码实现.由于代码比较多,一开始就全部介绍所有细节,估计很多人接受不了,也理解不了.所以,我先进行一 ...

  5. 【HTML5】Web Audio API打造超炫的音乐可视化效果

    HTML5真是太多炫酷的东西了,其中Web Audio API算一个,琢磨着弄了个音乐可视化的demo,先上效果图: 项目演示:别说话,点我!  源码已经挂到github上了,有兴趣的同学也可以去st ...

  6. 一个App完成入门篇(七)- 完成发现页面

    第七章是入门篇的倒数第二篇文章了,明天整个APP将进入收官. 本节教程主要要教会大家使用二维码扫描和用do_WebView组件加在html页面. 导入项目 do_WebView组件 扫描功能 自定义事 ...

  7. ASP.NET MVC 5 Web编程2 -- URL映射(路由原理)

    本章将讲述ASP.NET MVC5 的路由原理,即URL映射机制. 简单点就是解释:为什么MVC在浏览器输入地址就能访问到类(或类中的方法)?这是怎么做到的?我自己可以通过.NET写出一个自己的MVC ...

  8. Linux服务器配置之加载硬盘

    Linux服务器配置之加载硬盘 1.修改密码 passwd 2.测试密码是否成功 3.查看硬盘信息 fdisk –l 4.格式化分区 fdisk /dev/vdb 5.查看分区 6.快速格式化/dev ...

  9. 《Entity Framework 6 Recipes》中文翻译系列 (27) ------ 第五章 加载实体和导航属性之关联实体过滤、排序、执行聚合操作

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 5-9  关联实体过滤和排序 问题 你有一实体的实例,你想加载应用了过滤和排序的相关 ...

  10. LeetCode Note 1st,practice makes perfect

    1. Two Sum Given an array of integers, return indices of the two numbers such that they add up to a ...