一个WCF服务可以实现多个服务协定(服务协定实为接口),不过,每个终结点只能与一个服务协定关联,并指定调用的唯一地址。那么,binding是干吗的?binding是负责描述通信的协议,以及消息是否加密等内容。

好,不扯F话,说说今天的主题——OperationContextScope,这是一个类,而且是实现了 IDisposable 接口,说明这个类在实例化后,可能会持有某些特定的状态信息,在释放实例时需要进行清理。

这个猜测很对,OperationContextScope类的作用其实就是这样。说具体一点,就是在这个类实例化后,到它被释放之间形成一个代码范围,在这个特定的范围内,可以对正在调用的服务操作进行访问,最典型的做法是在这个范围内修改消息头,通常是添加自定义的消息头。

那为什么要用OperationContextScope来圈定一个范围来修改消息头呢?因为这样做可以保证只有在这一次调用服务操作才会添加自定义的消息头,在其他地方调用则不会添加自定义头。

一般来说,自定义消息头是用来附加一些额外的信息,这些数据不属于服务操作正文部分,并且是可有可无的。有点像发电子邮件时的附件,附件是可有可无的,可以与邮件正文有关,也可以与正文无关。

好,理论说完了。WCF相关的文章,我之所以写得少,就是因为它难写,WCF本身就涉及到很多Web服务相关的标准和概念,理解起来也不容易,而理论部分总是让人越看越不懂。经过老周K年时间的摸爬滚打,总结出来最有用的编程学习办法就是——写代码。虽然听起来是句F话,但是,真的找不到比这一招更好的办法。就拿老周学习WCF的过程来说吧,尽管许多概念可以网上查,可是看了之后呢,懂吗?还是不懂,哪些读了与Web服务相关的专著,还是不懂;哪怕再看一遍MSDN,似乎还是不懂。那怎么办,无他,硬着头皮敲代码。理论方面的东西弄不懂,难道连代码也不会写了不成?嘿,这果然是个好招儿,本来不懂的东东,把代码一写,果然就有感觉了。

道理一样,要搞懂OperationContextScope类是个什么货色,光用嘴说太抽象,但是,把代码往VS里面一写,我相信你会马上懂了。不信?咱们试试。

按照正常人类思维方式,我们应当先做服务器端。

先弄个服务协定,当然,它是一个接口,这个基础相信大家是有的。

    [ServiceContract(ConfigurationName ="ct",Namespace ="http://dog.org", Name ="哈吧dog")]
interface ITest
{
[OperationContract(Name = "wang")]
string SaySomthing(string name);
}

服务操作很简单,传入一个字符串,返回一个字符串。哦,对了,这里有个玩意儿可能大家比较陌生,ConfigurationName ="ct" 是个什么鬼?首先,我声明一下,它不是鬼;再者,它呢,你可以随便指定一个名字,最好是简短的,方便你记住的,因为等会儿在写配置文件时有用,这里我取了个名字叫ct。

然后,理所当然,就是实现服务协定。

    [ServiceBehavior(ConfigurationName = "sv")]
public class Service : ITest
{
public string SaySomthing(string name)
{
Console.WriteLine("\n========= 操作被调用 ===========");
OperationContext context = OperationContext.Current;
var hds = context.IncomingMessageHeaders;
StringBuilder strb = new StringBuilder();
foreach (var h in hds)
{
strb.AppendLine($"【{h.Namespace} - {h.Name}】: {hds.GetHeader<string>(h.Name, h.Namespace)}");
}
Console.WriteLine(strb);
return $"旺旺!{name}。";
}
}

在实现 SaySomething 方法过程中,我做了些手脚,通过OperationContext的InComingMessageHeaders属性得到了客户端发送过来的消息的Header列表,然后在屏幕上输出每个Header的信息,包括命名空间,名称,以及内容。Header的内容可以通过GetHeader<T>方法来获取,T是返回内容的类型,如果希望把header的内容以字符串形式返回,就指定string。

和刚才服务协定定义相似,可能大家又看到了,我在类上应用了ServiceBehavior特性,又搞了个ConfigurationName,它的名字叫 sv, 同样,也是在配置文件上使用的。

最后,创建ServiceHost,并打开服务。

            using (ServiceHost host=new ServiceHost(typeof(Service)))
{
try
{
host.Open();
Console.WriteLine("服务已启动。");
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.Read();
}

一般在演示示例时,我不用IIS来承载,麻烦,直接弄个控制台应用程序来启动服务,方便。

下面,开始配置一个服务的配置文件。

  <system.serviceModel>
<services>
<service name="sv">
<endpoint address="http://127.0.0.1:1000/mt" contract="ct" binding="basicHttpBinding" />
</service>
</services>
</system.serviceModel>

看出来了没,现在知道我刚才搞的两个ConfigurationName的作用了吧。没看出来?我给你讲讲。

先看 service 元素,通常,name应该指定服务类的全名,包括命名空间,比如我刚刚的类名为Service,这里应写上 name = "MyNamespace.Service",不过,因为我刚才用ServiceBehaviorAttribute指定了ConfigurationName叫 sv,所以在配置文件中,我不用再写一大串的类型名称了,只写上 sv 就完事了。

一样的道理,endpoint是必须与一个服务协定关联的,刚才在定义服务协定时,我也用ConfigurationName给了一个名字叫 ct,故这里的 contract = "ct" 就是指向ITest协定接口了,如果不指定ConfigurationName,那么在配置endpoint元素时,你只好把接口的路径写全了,即contract = "MyNamespace.ITest",但这里我可以轻松写成ct就可以了。

Good,服务端完成,现在做客户端。先给客户端的配置文件写一下。

  <system.serviceModel>
<client>
<endpoint name="ep" address="http://127.0.0.1:1000/mt" contract="ct" binding="basicHttpBinding"/>
</client>
</system.serviceModel>

然后,在客户端代码中重定义服务协定,你可以让服务器和客户端共享服务协定,当然也可以重新定义,接口和成员方法的名字可以不同,只要参数类型,个数和顺序对上就行。当然了,协定的命名空间和名称要与服务端一致。

    [ServiceContract(Namespace = "http://dog.org", Name = "哈吧dog", ConfigurationName = "ct")]
interface IDemo : IClientChannel
{
[OperationContract(Name = "wang")]
string SpeakTo(string name);
}

这里我让它继承了IClientChannel接口,因为待会儿要用。在客户端,只定义接口就行了,不用实现,运行时库会自动完成。你也不用实现IClientChannel接口,因为WCF内部已经有实现类了。

接着,用ChannelFactory<IDemo>来创建通道,通道类型就是协定类型接口。

        ChannelFactory<IDemo> fac = null;
IDemo channel = null; …………
fac = new ChannelFactory<IDemo>("ep");
channel = fac.CreateChannel(); // 调用完后,记得X掉它们
channel?.Close();
fac?.Close();

调用CreateChannel方法就能得到实现了IDemo接口的实例,这个内部会自动完成,你可以不管。在调用Close方法时,变量名后面多了个?,这是C# 6的新玩法,意思就是如果变量引用的是null,那代码就不执行了。

现在,我们用同一个通道实例,对服务进行两次调用。

            using (OperationContextScope scope = new OperationContextScope(channel))
{
// 获取当前操作上下文
OperationContext context = OperationContext.Current;
// 添加新的消息头
MessageHeader hd = MessageHeader.CreateHeader("add_msg", "http://www.dog.net", "这是一条德国进口犬");
context.OutgoingMessageHeaders.Add(hd);
// 调用服务操作
lblMsg.Text += channel.SpeakTo("杰克") + "\n";
}
// 再次调用
lblMsg.Text += channel.SpeakTo("肯肯");

第一次调用,是在OperationContextScope所划定的范围内进行的,并且向消息添加了个自定义Header;而第二次调用是在Scope范围之外的。

两次调用后,看看服务器的输出内容,你就能发现什么新事情了。

看出来了吧,第一次调用多了个Header,而第二次调用没有。看看两次发出的消息。

回忆一下,我们刚才两次调用服务,用的是同一个通道实例——channel,它在第一次调用时有自定义hd,而第二次调用时,自定义hd不见了。

从这个例子的比较中,你就知道OperationContextScope的用途了。Scope所圈定的范围内所做的修改只在该范围内有效,当跳出这个Scope,你再调用服务,Operation的状态会自动被还原。

在Scope中调用,给消息加了自定义的头,但只在这个范围内有效,等代码走出Scope后,对Operation的调用就会自动变回原来的样子,所以,自定义的头部不复存在。正因为如此,在服务器上的输出中,第一次调用会有自定义头,而第二次没有自定义头。第一次调用时,客户端在Scope中添加了自定义头,而第二次调用是在Scope之外,消息状态被还原,自定义头就不见了。

老周只能讲到这儿了,能不能懂,真的看你的理解了,前面都说了,WCF的东西真的很难讲解。如果对OperationContextScope还不理解,可以把示例反复研究一下,示例中我调用了两次服务作对比,如果你不理解,可以改代码,让客户端调用三次、四次。

示例代码下载地址

【.net深呼吸】(WCF)OperationContextScope 的用途的更多相关文章

  1. wcf之OperationContextScope

    作用:使用消息头向服务发送额外的信息. 1.客户端代码如下: namespace Client { class Program { static void Main(string[] args) { ...

  2. WCF契约定义及主要用途

    我们在使用WCF时,对其制定各种各样的规则,就叫做WCF契约.任何一个分布式的应用程序在传递消息的时候都需要实现制定一个规则. WCF配置文件相关操作技巧解析 全方位解读WCF Address配置文件 ...

  3. 【.net 深呼吸】记录WCF的通信消息

    前面老周给大伙伴们介绍了把跟踪信息写入日志文件的方法,今天咱们换个类似的话题来扯一下,对了,咱们就说说怎么把WCF的往来消息log下来吧. 尽管在现实生活中,我们不主张偷窥他人信息,不过,偷窥程序信息 ...

  4. 【.net 深呼吸】聊聊WCF服务返回XML或JSON格式数据

    有时候,为了让数据可以“跨国经营”,尤其是HTTP Web有关的东东,会将数据内容以 XML 或 JSON 的格式返回,这样一来,不管客户端平台是四大文明古国,还是处于蒙昧时代的原始部落,都可以使用这 ...

  5. 关于WEB Service&WCF&WebApi实现身份验证之WCF篇(2)

    因前段时间工作变动(换了新工作)及工作较忙暂时中断了该系列文章,今天难得有点空闲时间,就继续总结WCF身份验证的其它方法.前面总结了三种方法(详见:关于WEB Service&WCF& ...

  6. WCF 已知类型和泛型解析程序 KnownType

    数据协定继承 已知类型和泛型解析程序 Juval Lowy 下载代码示例 自首次发布以来,Windows Communication Foundation (WCF) 开发人员便必须处理数据协定继承方 ...

  7. WCF自定义扩展,以实现aop!

    引用地址:https://msdn.microsoft.com/zh-cn/magazine/cc163302.aspx  使用自定义行为扩展 WCF Aaron Skonnard 代码下载位置: S ...

  8. 重温WCF之数据契约中使用枚举(转载)(十一)

    转载地址:http://www.zhuli8.com/wcf/EnumMember.html 枚举类型的定义总是支持序列化的.当我们定义一个新的枚举时,不必应用DataContract特性,就可以在数 ...

  9. 重温WCF之发送和接收SOAP头(三)

    SOAP头可以理解为一种附加信息,就是附加到消息正文的内容. 既然消息头是附加信息,那有啥用呢?你可别说,有时候还真有不少用处.举个例子,WCF的身份验证是不是很麻烦?还要颁发什么证书的(当然不是荣誉 ...

随机推荐

  1. Python编码记录

    字节流和字符串 当使用Python定义一个字符串时,实际会存储一个字节串: "abc"--[97][98][99] python2.x默认会把所有的字符串当做ASCII码来对待,但 ...

  2. Jquery的点击事件,三句代码完成全选事件

    先来看一下Js和Jquery的点击事件 举两个简单的例子 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  3. HTML5 Boilerplate - 让页面有个好的开始

    最近看到了HTML5 Boilerplate模版,系统的学习与了解了一下.在各种CSS库.JS框架层出不穷的今天,能看到这么好的HTML模版,感觉甚爽.写篇博客,推荐给大家使用.   一:HTML5 ...

  4. Sublime Text3配置在可交互环境下运行python快捷键

    安装插件 在Sublime Text3下面写代码感觉很不错,但是写Python的时候遇到了一些问题. 用Sublime Text3打开python文件,或者在Sublime Text3下写好pytho ...

  5. ExtJS 4.2 介绍

    本篇介绍ExtJS相关知识,是以ExtJS4.2.1版本为基础进行说明,包括:ExtJS的特点.MVC模式.4.2.1GPL版本资源的下载和说明以及4种主题的演示. 目录 1. 介绍 1.1 说明 1 ...

  6. 获取微软原版“Windows 10 推送器(GWX)” 卸载工具

    背景: 随着Windows 10 免费更新的结束,针对之前提供推送通知的工具(以下简称GWX)来说使命已经结束,假设您还未将Windows 8.1 和Windows 7 更新到Windows 10 的 ...

  7. beans.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  8. eclipse — 导入android项目后识别成java项目的问题及解决

    最近在eclipse导入android项目的时候遇到了奇葩问题,再此记录 遇到的问题就是:将完好的android项目导入到eclipse的时候,原本这是一个很容易的事情,但是导入成功后发现,,,靠ec ...

  9. Android 开发一定要看的15个实战项目

    前言: 虽说网上有太多的Android课程,但是大多都是视频,有Android在线开发环境的几乎没有,但是对于学习Android的人来说拥有在线的Android开发环境是非常好的,可以随时动手操作学习 ...

  10. 监控 SQL Server (2005/2008) 的运行状况

    Microsoft SQL Server 2005 提供了一些工具来监控数据库.方法之一是动态管理视图.动态管理视图 (DMV) 和动态管理函数 (DMF) 返回的服务器状态信息可用于监控服务器实例的 ...