定义服务契约

  构建HelloWCF应用的第一步是创建服务契约。契约式是表示消息应用外形的主要方式。对于外形,是指服务暴露的操作,使用的消息 schema和每个操作实现的消息交换模式(MEP)。总之,契约定义了我们消息应用生成和使用的东西。大多数契约是带有WCF API定义的属性标记的类型定义。

  在下面的例子里,服务契约是一个带有System.ServiceModel.ServiceContractAttribute 和System.ServiceModel.OperationContractAttribute标记的接口,如下所示:

[ServiceContract]
public interface IHelloWCF {
 [OperationContract]
 void Say(String input);
}

  在高层次上,我们的服务契约表示我们接收消息的应用包含一个名字为Say的操作,并且这个操作接收一个String类型的参数和void返回类 型。发送消息的应用可以用它来构造和发送消息给接收程序。既然我们已经定义的服务契约,那就到了该定义接收程序侦听地址和如何与其它消息参与者交换消息的 时候了。

定义地址和绑定

  定义侦听请求消息的地址需要使用System.Uri类型,定义如何与其它消息参与者交换消息需要我们使用 System.ServiceModel.Channels.Binding类型。或者这些类型的继承类型。下面的代码说明了如何在我们的应用里使用 Uri和Binding类型。

static void Main(){
 // define where to listen for messages定义侦听消息的地址
 Uri address = new Uri("http://localhost:8000/IHelloWCF");
  
 // define how to exchange messages定义如何交换消息
 BasicHttpBinding binding = new BasicHttpBinding();
}

  注意局部变量address使用的是HTTP格式的统一资源标识符(URI)。选择这个地址强制要求我们使用HTTP传输。更高层次上,绑定是 指定传输、消息编排和消息编码的主要方式。局部变量binding是BasicHttpBinding类型的实例。和你从名字看到的一 样,BasicHttpBinding创建的是一个用于HTTP传输的消息架构。

创建一个终结点并启动侦听

  接下来我们要使用地址(address)、绑定(binding)和契约(contract)来构建一个终结点(endpoint)并在此终结 点上侦听发送进来的消息。通常来说,一个WCF接受程序可以构建和使用多个终结点,并且每个终结点都需要一个地址、一个绑定和一个契约。 System.ServiceModel.ServiceHost类型构建和托管终结点,并管理接受应用底层结构的其他部分,比如线程和对象的生命周期。 下面代码块演示了如何实例化ServiceHost,如何添加终结点和如何开始侦听进入的消息:

static void Main(){
   // define where to listen for messages定义侦听消息的地址
   Uri address = new Uri("http://localhost:4000/IHelloWCF");
   // define how to exchange messages定义如何交换消息
   BasicHttpBinding binding = new BasicHttpBinding();
   // instantiate a ServiceHost, passing the type to instantiate实例化ServiceHost,传递服务类型
   // when the application receives a message
   ServiceHost svc = new ServiceHost(typeof(HelloWCF));
   // add an endpoint, passing the address, binding, and contract增加终结点、绑定和契约
   svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);
   // begin listening开始侦听
   svc.Open();
   // indicate that the receiving application is ready and指示应用准备接受消息
   // keep the application from exiting immediately保持应用程序不会立即退出
   Console.WriteLine("The HelloWCF receiving application is ready");
   Console.ReadLine();
   // close the service host关闭宿主
   svc.Close();
}

  上述代码里调用了svc.AddServiceEndpoint 和svc.Open。AddServiceEndpoint实例方法设置ServiceHost对象的属性,这样它将使用地址、绑定和契约参数执行的行为 来侦听消息。要着重指出的是AddServiceEndpoint方法没有开始循环侦听;它仅仅是简单地改变了ServiceHost对象的状态。 ServiceHost实例的Open方法构建了消息基础结构,并开始循环侦听。Open方法会验证ServiceHost对象的状态,从它的状态里构建 终结点,并且开始侦听。

映射接收的消息到HelloWCF的成员

  在目前状态,我们编译程序,当程序试图构建一个终结点的时候,会出现一个异常:InvalidOperationException。原因一目 了然:在ServiceHost类型的构造函数里,我们传递了HelloWCF作为参数,因此,这就表示消息基础结构要分发消息给我们的HelloWCF 对象。因此,必然存在消息到服务成员的映射关系。最简单的创建映射的方式就是使HelloWCF服务类实现服务契约IHelloWCF。

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
  
// implement the IHelloWCF service contract
sealed class HelloWCF : IHelloWCF {
 // indicate when a HelloWCF object is created
 HelloWCF() { Console.WriteLine("HelloWCF object created"); }
  
 static void Main(){
    // define where to listen for messages
    Uri address = new Uri("http://localhost:4000/IHelloWCF");
    // define how to exchange messages
    BasicHttpBinding binding = new BasicHttpBinding();
    // instantiate a ServiceHost, passing the type to instantiate
    // when the application receives a message
    ServiceHost svc = new ServiceHost(typeof(HelloWCF));
    // add an endpoint, passing the address, binding, and contract
    svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);
    // begin listening
    svc.Open();
    // indicate that the receiving application is ready and
    // keep the application from exiting immediately
    Console.WriteLine("The HelloWCF receiving application is ready");
    // wait for incoming messages
    Console.ReadLine();
    // close the service host
    svc.Close();
 }
  
 // received messages are dispatched to this instance
 // method as per the service contract
 public void Say(String input){
    Console.WriteLine("Message received, the body contains: {0}", input);
 }
}
  
[ServiceContract]
public interface IHelloWCF {
 [OperationContract]
 void Say(String input);
}

  改变HelloWCF的类型定义会使得消息的基础结构分发接受到的消息到服务实例的Say操作上,因此会在控制台界面上输出一个简单的语句。

编译、运行和检验接受者

  我们现在准备使用下面的命令行编译并运行这个应用:

C:\temp>csc /nologo /r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\Windows Communication
Foundation\System.ServiceModel.dll" HelloWCFApp.cs
  
C:\temp>HelloWCFApp.exe
The HelloWCF receiving application is ready

  此时,接受消息的应用在被动地等待请求消息的到来。我们是用netstat.exe可以检查一下应用是否确实在侦听,如下所示:

c:\temp>netstat –a –b
TCP    kermit:4000            0.0.0.0:0              LISTENING       1104
[HelloWCFApp.exe]

向接受者发送消息

  发送消息的基础结构也需要依靠地址、绑定和契约,这与接收消息的基础结构类似。

  与接受者不同,绝大多数发送者放弃直接使用Uri类型,而是使用System.ServiceModel.EndpointAddress类型去表示消息发送的目标。

  EndpointAddress类型是WCF对于WS-Addressing 终结点参考的抽象。此外,发送者不使用ServiceHost类型,而是使用ChannelFactory<T>类型(T是服务契约类型)。 ChannelFactory<T>类型构建发送消息的基础结构和ServiceHost构建接受消息的基础结构类似。下面的代码演示了如何 使用EndpointAddress类型和ChannelFactory<T>构建发送基础结构。

using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
  
// implement the IHelloWCF service contract
sealed class HelloWCF : IHelloWCF {
 // indicate when a HelloWCF object is created
 HelloWCF() { Console.WriteLine("HelloWCF object created"); }
 static void Main(){
    // define where to listen for messages
    Uri address = new Uri("http://localhost:4000/IHelloWCF");
    // define how to exchange messages
    BasicHttpBinding binding = new BasicHttpBinding();
    // instantiate a ServiceHost, passing the type to instantiate
    // when the application receives a message
    ServiceHost svc = new ServiceHost(typeof(HelloWCF));
    // add an endpoint, passing the address, binding, and contract
    svc.AddServiceEndpoint(typeof(IHelloWCF), binding, address);
    // begin listening
    svc.Open();
    // indicate that the receiving application is ready and
    // keep the application from exiting immediately
    Console.WriteLine("The HelloWCF receiving application is ready");
  
    // begin the sender code发送者代码开始
    // create a channelFactory<T> with binding and address
    ChannelFactory<IHelloWCF> factory =
      new ChannelFactory<IHelloWCF>(binding,
                                    new EndpointAddress(address));
    // use the factory to create a proxy使用工厂创建代理
    IHelloWCF proxy = factory.CreateChannel();
    // use the proxy to send a message to the receiver使用代理发送消息给接受者
    proxy.Say("Hi there WCF");
    // end the sender code发送者代码结束
  
    Console.ReadLine();
    // close the service host
    svc.Close();
 }
  
 // received messages are dispatched to this instance
 // method as per the service contract
 public void Say(String input){
    Console.WriteLine("Message received, the body contains: {0}", input);
 }
}
  
[ServiceContract]
public interface IHelloWCF {
 [OperationContract]
 void Say(String input);
}

  更高层次上,ChannelFactory<T>对象是一个可以制造产生和发送消息给接受者(因此需要在构造函数里传递绑定和地 址)的基础结构的类型。ChannelFactory<T>实例的CreateChannel方法实际创建的是发送基础结构,并且通过实现服 务契约的一个对象返回这个基础结构的引用。

编译、运行和检验发送者

  既然我们已经完成了发送和接受基础结构代码,现在应该是编译和运行程序的时候了,如下所示:

c:\temp>csc /nologo /r:"c:\WINDOWS\Microsoft.Net\Framework\v3.0\Windows Communication
Foundation\System.ServiceModel.dll" HelloWCFApp.cs
  
c:\temp>HelloWCFApp.exe
The HelloWCF receiving application is ready
HelloWCF object created
Message received, the body contains: HelloWCF!

  如期望的一样,我们的程序执行步骤如下:

  1.    为接受来自http://localhost:4000/IHelloWCF的消息构建基础结构。

  2.    开始在http://localhost:4000/IHelloWCF上侦听消息。

  3.    构建发送到http://localhost:4000/IHelloWCF消息的基础结构。

  4.    生成和发送消息到http://localhost:4000/IHelloWCF。

  5.    接受消息,实例化一个HelloWCF对象,分发消息到服务实例的Say方法上。

看一下消息

  现在代码写完了,貌似没看到我们HelloWCF例子里哪里使用到了消息。对于开发者,一个WCF应用看起来和感觉都很像面向对象或者面向组件 的应用。在运行时,WCF应用要生成、发送和接受消息,同样也要处理消息。通过修改Say方法的实现我们能看到WCF接触结构的消息:

public void Say(String input){
 Console.WriteLine("Message received, the body contains: {0}", input);
 // Show the contents of the received message显示接受消息内容
 Console.WriteLine(
    OperationContext.Current.RequestContext.RequestMessage.ToString());
}

  修改Say方法后的输出如下:

The HelloWCF receiving application is ready
HelloWCF object created
Message received, the body contains: HelloWCF!
 <s:Header>
    <To s:mustUnderstand="1"
      http://localhost:8000/IHelloWCF
    </To>
    <Action s:mustUnderstand="1"
         http://tempuri.org/IHelloWCF/Say
     </Action>
 </s:Header>
 <s:Body>
    <Say xmlns="http://tempuri.org/">
      <input>HelloWCF!</input>
    </Say>
 </s:Body>
</s:Envelope>

  注意到打印的SOAP消息,消息的Body部分包含我们传递给局部变量的channel 上Say方法的字符串。宏观上讲,我们的应用程序使用这个字符来构建一个SOAP消息,然后发送这个SOAP消息到我们程序的接受部分。接受部分,换句话 说,它要接受SOAP消息,创建一个HelloWCF实例,提取SOAP Body的内容,调用HelloWCF 实例的Say方法,传递字符串参数。

暴露元数据

  我们的Hello WCF程序使用一个相当简单的方法就实现了接收者和发送者之间的兼容性。因为接收者和发送者驻留在同一个AppDomain里,并且接收者使用的对象对于 发送者来说是可见的,我们在发送者代码里简单地重用了地址、绑定和契约。在发部分消息应用里,这个方法是可行的。绝大多数情况,我们希望发送者和接收者去 驻留在不同的机器上的AppDomains里。在这些场景里,接收者显示指定消息需求,发送者遵守这些需求。

  WS- MetadataExchange规范规定了发送者和接收者在平台无关时如何交换这些数据信息。在更多的条款里,WS-MetadataExchange 规范限定了方便终结点之间进行消息交换的schema和编排。在大部分现实世界的应用系统里,有一个暴露信息方式的需求,这个方式就是发送者询问接收者的 终结点去提取元数据,并使用这些元数据构建能发送给接收终结点消息的基础结构。

  WCF缺省的情况下不会暴露元数据,这些原因都是因为对安全的考虑。元数据暴露的信息包含应用系统的安全需求。以保护秘密的名义,这个团队选择 缺省情况下关闭这个特性。如果决定暴露系统的元数据,我们可以构建一个暴露元数据的终结点。构建元数据终结点的方式和其它终结点非常相似:使用地址、绑定 和契约。但是目前为止你看到的终结点不太一样,就是服务契约已经定义到WCF的API里了。

  构建元数据终结点的第一步是修改ServiceHost到可以托管元数据的状态。我们可以通过System.ServiceModel. Description.ServiceMetadataBehavior对象增加到ServiceHost行为集合里。行为是WCF基础结构用来改变本 地消息处理的特定信息。下面代码演示了如何增加ServiceMetadataBehavior对象到活动的ServiceHost对象:

// instantiate a ServiceHost, passing the type to instantiate
// when the application receives a
messageServiceHost svc = new ServiceHost(typeof(HelloWCF), address);
// BEGIN NEW METADATA CODE
// create a ServiceMetadataBehavior创建服务元数据行为
ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
metadata.HttpGetEnabled = true;
// add it to the servicehost description
svc.Description.Behaviors.Add(metadata);

  下一步就是为元数据终结点定义Binding。元数据绑定的对象模型与其它绑定区别很大,我们通过调用工厂方法上的System.ServiceModel.Description.MetadataExchangeBindings的类型创建元数据绑定,如下所示:

// instantiate a ServiceHost, passing the type to instantiate
// when the application receives a message
ServiceHost svc = new ServiceHost(typeof(HelloWCF));
  
// BEGIN NEW METADATA CODE
// create a ServiceMetadataBehavior创建服务元数据行为
ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
// add it to the servicehost description
svc.Description.Behaviors.Add(metadata);
// create a TCP metadata binding创建TCP元数据绑定
Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
 
由 于ASMX(ASP.NET Web Service)的影响,你也许会认为元数据只能通过HTTP传输呈现。事实上,元数据可以通过多种传输协议传递,并且WS- MetadataExchange说明了这个灵活性。在我们的例子里,我们调用CreateMexTcpBinding方法,它返回了一个继承自 Binding类型的TCP 传输绑定。因为我们使用的是TCP传输,所以我们必须确保元数据地址使用了TCP地址格式,如下所示:
 
// instantiate a ServiceHost, passing the type to instantiate
 
// when the application receives a message
ServiceHost svc = new ServiceHost(typeof(HelloWCF));
  
// BEGIN NEW METADATA CODE
// create a ServiceMetadataBehavior
ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
// add it to the servicehost description
svc.Description.Behaviors.Add(metadata);
// create a TCP metadata binding
Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
// create an address to listen on WS-Metadata exchange traffic
//创建元数据交换侦听地址
Uri mexAddress = new Uri("net.tcp://localhost:5000/IHelloWCF/Mex");

  既然我们定义了元数据终结点需要的地址和绑定,我们要添加终结点到ServiceHost上,方式很像我们定义的第一个消息终结点。当添加元数 据终结点时,我们要使用WCF API定义的名为System.ServiceModel.Description.IMetadataExchange的服务契约。下面代码演示了如何 添加一个元数据终结点到ServiceHost上,使用适当的地址、绑定和契约。

// instantiate a ServiceHost, passing the type to instantiate
// when the application receives a message
ServiceHost svc = new ServiceHost(typeof(HelloWCF));
  
// BEGIN NEW METADATA CODE
// create a ServiceMetadataBehavior
ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
// add it to the servicehost description
svc.Description.Behaviors.Add(metadata);
// create a TCP metadata binding
Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
// create an address to listen on WS-Metadata exchange traffic
Uri mexAddress = new Uri("net.tcp://localhost:5000/IHelloWCF/Mex");
// add the metadata endpoint添加元数据终结点
svc.AddServiceEndpoint(typeof(IMetadataExchange),
                       mexBinding,
                       mexAddress);
// END METADATA CODE

使用元数据

  Microsoft .NET Framework SDK安装了一个功能强大的工具名字是svcutil.exe,它的一个功能就是询问一个运行的消息应用并基于获得的信息生成代理。从内部来 说,svcutil.exe使用的是WS-MetadataExchange协议,像与ASMX一起普及的WSDL里的“get”语义一样。因为我们的接 收程序暴露了一个元数据终结点,我们可以把svcutil.exe指向这个运行的终结点,svcutil.exe会自动生成一个代理类型和与服务终结点兼 容的配置信息,这些信息都是参考元数据生成。当使用这种方式的时候,svcutil.exe 依照WS-MetadataExchange的方式发送消息给接收程序,并转化这些消息为.NET Framework的类型以方便发送消息程序的开发。

使用Svcutil.exe生成代理

  在你运行svcutil.exe以前,检查一下HelloWCFApp.exe正常运行并侦听请求消息。下一步就是打开一个新的Windows SDK命令行窗口,输入下面的命令:

C:\temp>svcutil /target:code net.tcp://localhost:5000/IHelloWCF/Mex

  Svcutil.exe会创建2个文件:HelloWCFProxy.cs 和output.config。如果你检查一下HelloWCFProxy.cs文件,你就会看到svcutil.exe产生了一个包含 IHelloWCF、IHelloWCFChannel,和HelloWCFClient的代码文件。合起来看,这些类型定义就是帮助我们创建于接受程序 兼容的发送代码。

http://www.cnblogs.com/tianzhiliang/archive/2011/08/09/2132532.html

WCF 快速入门的更多相关文章

  1. 快速入门系列--WebAPI--01基础

    ASP.NET MVC和WebAPI已经是.NET Web部分的主流,刚开始时两个公用同一个管道,之后为了更加的轻量化(WebAPI是对WCF Restful的轻量化),WebAPI使用了新的管道,因 ...

  2. 快速入门系列--WebAPI--03框架你值得拥有

    接下来进入的是俺在ASP.NET学习中最重要的WebAPI部分,在现在流行的互联网场景下,WebAPI可以和HTML5.单页应用程序SPA等技术和理念很好的结合在一起.所谓ASP.NET WebAPI ...

  3. 快速入门系列--WebAPI--04在老版本MVC4下的调整

    WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.N ...

  4. 快速入门系列--MVC--01概述

    虽然使用MVC已经不少年,相关技术的学习进行了多次,但是很多技术思路的理解其实都不够深入.其实就在MVC框架中有很多设计模式和设计思路的体现,例如DependencyResolver类就包含我们常见的 ...

  5. C#面向服务编程技术WCF从入门到实战演练

    一.WCF课程介绍 1.1.Web Service会被WCF取代吗? 对于这个问题阿笨的回答是:两者在功能特性上却是有新旧之分,但是对于特定的系统,适合自己的就是最好的.不能哪一个技术框架和行业标准作 ...

  6. [转]快速入门系列--WebAPI--01基础

    本文转自:http://www.cnblogs.com/wanliwang01/p/aspnet_webapi_base01.html ASP.NET MVC和WebAPI已经是.NET Web部分的 ...

  7. Expression Blend实例中文教程(2) - 界面快速入门

    上一篇主要介绍Expression系列产品,另外概述了Blend的强大功能,本篇将用Blend 3创建一个新Silverlight项目,通过创建的过程,对Blend进行快速入门学习. 在开始使用Ble ...

  8. .Net Core 3.0 IdentityServer4 快速入门

    .Net Core 3.0 IdentityServer4 快速入门 一.简介 IdentityServer4是用于ASP.NET Core的OpenID Connect和OAuth 2.0框架. 将 ...

  9. Web Api 入门实战 (快速入门+工具使用+不依赖IIS)

    平台之大势何人能挡? 带着你的Net飞奔吧!:http://www.cnblogs.com/dunitian/p/4822808.html 屁话我也就不多说了,什么简介的也省了,直接简单概括+demo ...

随机推荐

  1. PhpStorm 10.0.3破解版下载

    汉化破解版软件下载: http://pan.baidu.com/s/1geNO24r 密码: d5ci 这个汉化破解软件解决了大纲视图里空白的问题. 先安装腾讯电脑管家,然后安装这个软件,安装到最后提 ...

  2. 20145104张家明 《Java程序设计》第6周学习总结

    20145104张家明 <Java程序设计>第6周学习总结 教材学习内容总结 第10章与11章总结 标准输入输出 System.in: 标准输入,默认关联到键盘(终端输入) System. ...

  3. 2017-2018-1 JaWorld 团队作业--冲刺3

    2017-2018-1 JaWorld 团队作业--冲刺3 (20162306) 总体架构 我们本次团队项目设定为基于Android系统Java架构下的打飞机小游戏 游戏中所有模型的原型设定是精灵,因 ...

  4. Git简介【转】

    本文转载自:http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 Git简介 Git是什 ...

  5. Cannot perform runtime binding on a null reference

    一个方法的参数是dynamic  obj 方法内调用了obj.Header 但是外部传递进来的obj是null,然后就会报这个错.

  6. P4 PI库安装

    repo:Github PI P4Runtime - a control plane framework and tools for the P4 programming language 这个是P4 ...

  7. BZOJ4811 [Ynoi2017]由乃的OJ

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  8. bzoj 2818 gcd 线性欧拉函数

    2818: Gcd Time Limit: 10 Sec  Memory Limit: 256 MB[Submit][Status][Discuss] Description 给定整数N,求1< ...

  9. 【安全测试】安全测试威胁建模设计方法STRIDE

    背景 目前安全测试一般都存在如下问题: 安全测试人员不懂业务,业务测试人员不懂安全,安全测试设计出现遗漏是无法避免的 安全测试点繁多复杂,单点分析会导致风险暴露,不安全 目前的状态: TR2阶段测试人 ...

  10. H5 canvas建造敌人坦克

      接着上一篇(http://www.cnblogs.com/zhouhuan/p/H5_tankgame3.html),这一篇建造敌人的坦克. 思路是,基于可扩展性和性能等方面的考虑,用构造函数改造 ...