一个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. 【原】FMDB源码阅读(三)

    [原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...

  2. 一起来玩echarts系列(一)------箱线图的分析与绘制

    一.箱线图 Box-plot 箱线图一般被用作显示数据分散情况.具体是计算一组数据的中位数.25%分位数.75%分位数.上边界.下边界,来将数据从大到小排列,直观展示数据整体的分布情况. 大部分正常数 ...

  3. Tcp/ip 报文解析

    在编写网络程序时,常使用TCP协议.那么一个tcp包到底由哪些东西构成的呢?其实一个TCP包,首先需要通过IP协议承载,而IP报文,又需要通过以太网传送.下面我们来看看几种协议头的构成 一 .Ethe ...

  4. ABP文档 - SignalR 集成

    文档目录 本节内容: 简介 安装 服务端 客户端 连接确立 内置功能 通知 在线客户端 帕斯卡 vs 骆峰式 你的SignalR代码 简介 使用Abp.Web.SignalR nuget包,使基于应用 ...

  5. 15个关于Chrome的开发必备小技巧[译]

    谷歌Chrome,是当前最流行且被众多web开发人员使用的浏览器.最快六周就更新发布一次以及伴随着它不断强大的开发组件,使得Chrome成为你必备的开发工具.例如,在线编辑CSS,console以及d ...

  6. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  7. SQLServer 版本之八大方法搞清 "我是谁"

    你正在使用 SQL Server 的哪个版本? 贴士:作为一个SQL Server数据库管理者或维护.支持人员,应该会经常问自己这样一个问题:我当前SQL Server版本号是?当前版本已经有的累计更 ...

  8. AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

    让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...

  9. 从Vue.js窥探前端行业

    近年来前端开发趋势 1.旧浏览器逐渐淘汰,移动端需求增加: 旧浏览器主要指的是IE6-IE8,它是不支持ES5特性的:IE9+.chrome.sarafi.firefox对ES5是完全支持的,移动端大 ...

  10. 负载均衡——nginx理论

     nginx是什么? nginx是一个强大的web服务器软件,用于处理高并发的http请求和作为反向代理服务器做负载均衡.具有高性能.轻量级.内存消耗少,强大的负载均衡能力等优势.  nginx架构? ...