WCF

一、什么是WCF?

1、Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口。它是.NET框架的一部分,由 .NET Framework[1] 3.0 开始引入,与 Windows Presentation Foundation及 Windows Workflow Foundation并行为新一代 Windows 操作系统以及 WinFX 的三个重大应用程序开发类库。

2、很久以前,有一家小商店,靠卖些水果过日子。竞争是如此激烈,为了生存,他们不得不自己进货,把货堆到自己的房间内,如果顾客需要,他们有时还不得不给顾客送货,总而言之,他们将所有该干的活都干了,只为了能赚到点生活费,这就是艰辛的人生。

一个web程序或者一个winform程序,简单模式的程序,我们通常都如那个水果店的老板一样,把所有的功能都集中到这个程序里,在简单的状况下,这很好。

水果店生意越来越好,老板的资金慢慢雄厚了,他注意到了卖其他东西比卖水果更赚钱,比如说家电、服装。于是,老板一口气又开了几家店。生意规模越来越大,钱也越赚越多,老板心花怒放。然而好景不长,亚洲金融风暴来袭,利润率急剧下降。老板忧心忡忡,既然外部开源不太可能,那就看看内部能不能节流了。老板考察一番,注意到,为了销售,每个店都配置了一个仓库,每个店都配置了一帮送货的人马,这,是不是太浪费了。于是,老板将所有的仓库撤销,成立了一个总仓库,不管是水果,家店,服装,都可以存储到这个仓库。管理一个仓库的费用比管理N个仓库的费用显然是要少很多的。然后,每个店的送货人员都辞掉,另外成立一个运输公司,专门负责送货,不过水果还是家店还是服装,装到纸箱后,他们都是一个样。经过这么一折腾,成本一下就降了下来,而且还便于管理了,真是一举两得,老板又绽放了笑容。

当程序涉及的范围越来越大时,也许就要考虑将服务分离出去。WCF是应对分布式开发的,就如水果店老板,生意大了后,他就是个分布式的了,这边一家水果店,那边一家服装店,他们之间有区别,卖的东西不同,也有共性,都是卖东西,不管是哪家店,他们都需要仓库,都需要送货。这时,你就可以单独成立公司,只提供这两种服务。如果写成程序,那么就如同你开发了一个运输的WCF,把这个服务放在服务器上,这样不管是谁,是Web程序也好,是Winform程序也好,只要接口对应,理解你的服务内容条款(服务契约),都可以要求你这个服务模块提供标准的服务。(来源于http://zhidao.baidu.com/question/148197639.html)

3、WCF 是一个分布式应用的开发框架,属于特定的技术,或者平台。既不是标准也不是规范。
Web Service:严格来说是行业标准,也就是Web Service 规范,也称作WS-*规范,既不是框架,也不是技术。 4、Visual Studio 2008及以后的版本才有wcf的功能。 二、怎么使用WCF?

一、定义服务契约

在这个实例中,我们创建一个简单的服务来管理员工的基本信息。至于实例程序的结构,我们依然采用熟悉的包含三个项目(Service.Interface、Service和Client)的解决方案。如下所示的是定义在Service.Interface中用于表示员工的Employee类的定义,它是一个数据契约。

   1: [DataContract(Namespace="http://www.artech.com/")]
   2: public class Employee
   3: {
   4:     [DataMember]
   5:     public string Id { get; set; }
   6:  
   7:     [DataMember]
   8:     public string Name { get; set; }
   9:  
  10:     [DataMember]
  11:     public string Department { get; set; }
  12:  
  13:     [DataMember]
  14:     public string Grade { get; set; }
  15:  
  16:     public override string ToString()
  17:     {
  18:         return string.Format("ID: {0,-5}姓名: {1, -5}级别: {2, -4} 部门: {3}",Id, Name, Grade, Department);
  19:     }
  20: }

接下来我们定义了如下一个表示服务契约的接口IEmployeesService。和基于SOAP的服务契约定义不同,我们无需在相应的操作方法上面应用OperationContractAttribute特性,但是应用在接口/类上的ServiceContractAttribute特性仍是必需的。在这里替换OperationContractAttribute特性的分别是WebGetAttributeWebInvokeAttribute,它们均定义在System.ServiceModel.Web程序集中。

   1: [ServiceContract(Namespace="http://www.artech.com/")]
   2: public interface IEmployees
   3: {
   4:     [WebGet(UriTemplate = "all")]
   5:     IEnumerable<Employee> GetAll();
   6:  
   7:     [WebGet(UriTemplate = "{id}")]
   8:     Employee Get(string id);
   9:  
  10:     [WebInvoke(UriTemplate = "/", Method = "POST")]
  11:     void Create(Employee employee);
  12:  
  13:     [WebInvoke(UriTemplate = "/", Method = "PUT")]
  14:     void Update(Employee employee);
  15:  
  16:     [WebInvoke(UriTemplate = "{id}", Method = "DELETE")]
  17:     void Delete(string id);
  18: }

契约接口IEmployeesService中定义了5个操作,分别用于实现针对员工数据的获取、添加、修改和删除。按照REST设计原则,我们将被操作的员工信息体现为某种网络资源,而操作类型最好与相应的HTTP方法相匹配。在操作方法中针对资源的操作类型与HTTP方法之间的匹配是通过应用在它们上面的WebGetAttribute和WebInvokeAttribute特性来体现。

WebGetAttribute针对GET方法,而其他的HTTP方法则通过WebInvokeAttribute的Method属性来体现。在IEmployeesService中,两个用于获取员工信息GetAll和Get方法均应用了WebGetAttribute特性,而其他的Create、Update和Delete方法在应用了WebInvokeAttribute特性,并且其Method属性被分别设置为PUT、POST和DELETE。

WebGetAttribute和WebInvokeAttribute和均具有相同的属性UriTemplate,该属性用于定义作为最终操作URI的模板。我们不仅可以通过UriTemplate属性为操作指定一个相对于终结点地址的静态路径,还可以通过占位符实现路径中的动态部分与参数之间的映射。

同样以定义在契约接口IEmployeesService中的5个操作方法为例,如果终结点地址为http://127.0.0.1:3721/employees,由于用于返回所有员工列表的GetAll操作的UriTemplate被设置“All”,所以其地址为http://127.0.0.1:3721/employees。用于返回指定员工ID的Get操作的UriTemplate被设置成“{id}”,意味着我们直接在表示请求地址的URI中指定员工的ID,而它会自动映射为该操作方法的参数id。用于删除某个指定员工的Delete操作具有相同的UriTemplate设置,而用于创建添加新员工和修改现有员工信息的Create和Update操作,由于作为参数的Employee对象具有ID属性,所以直接采用终结点地址。

二、创建/寄宿服务

在控制台程序Service中我们定义了如下一个实现了契约接口IEmployeesService的服务类型EmployeesService。简单 起见,我们直接通过一个静态字段employees表示存储的员工列表,该静态字段在初始化的工作中被添加了两个ID分别为001和002的Employee对象。针对员工信息的获取、添加、修改和删除的操作均在此列表中进行。

   1: public class EmployeesService : IEmployees
   2: {
   3:     private static IList<Employee> employees = new List<Employee>
   4:     {
   5:         new Employee{ Id = "001", Name="张三", Department="开发部", Grade = "G7"},    
   6:         new Employee{ Id = "002", Name="李四", Department="人事部", Grade = "G6"}
   7:     };
   8:     public Employee Get(string id)
   9:     {
  10:         Employee employee = employees.FirstOrDefault(e => e.Id == id);
  11:         if (null == employee)
  12:         {
  13:             WebOperationContext.Current.OutgoingResponse.StatusCode = 
  14:             HttpStatusCode.NotFound;
  15:         }
  16:         return employee;
  17:     }
  18:  
  19:     public void Create(Employee employee)
  20:     {
  21:         employees.Add(employee);
  22:     }
  23:  
  24:     public void Update(Employee employee)
  25:     {
  26:         this.Delete(employee.Id);
  27:         employees.Add(employee);
  28:     }
  29:  
  30:     public void Delete(string id)
  31:     {
  32:         Employee employee = this.Get(id);
  33:         if (null != employee)
  34:         {
  35:             employees.Remove(employee);
  36:         }
  37:     }
  38:  
  39:     public IEnumerable<Employee> GetAll()
  40:     {
  41:         return employees;
  42:     }
  43: }

值得一提的是,不论是用于获取某个指定ID的员工信息的Get方法,还是用于修改和删除员工记录的Update和Delete方法,当指定ID的员工不存在时都通过WebOperationContext表示当前Web操作上下文的对象将回复状态设置为NotFound(即404 Not Found),这体现了我们的服务是基于Web的。

接下来我们通过自我寄宿的方式对上面定义的EmployeesService服务进行寄宿,下面是相应的配置。我们为寄宿的服务添加了唯一一个终结点,并简单地指定了其ABC三要素。和我们之前配置的终结点不同的是,在这里我们采用的绑定类型为WebHttpBinding。

   1: <configuration>
   2:     <system.serviceModel>
   3:     <services>
   4:         <service name="Artech.WcfServices.Service.EmployeesService">
   5:         <endpoint address="http://127.0.0.1:3721/employees" 
   6:                   binding="webHttpBinding"
   7:                   contract="Artech.WcfServices.Service.Interface.IEmployees"/>
   8:         </service>
   9:     </services>
  10:     </system.serviceModel>
  11: </configuration>

最终我们通过如下的程序进行服务的寄宿。之前我们总是使用基于服务类型创建的ServiceHost进行服务寄宿,在这里我们使用的是ServiceHost它的子类WebServiceHost

   1: using (WebServiceHost host = new WebServiceHost(typeof(EmployeesService)))
   2: {
   3:     host.Open();
   4:     Console.Read();
   5: }

三、进行服务调用

由于我们寄宿的服务完全是基于Web的,所以和普通的Web站点没有本质的区别。由于EmployeesService服务的GetAll和Get操作支持HTTP-GET请求,所以我们完全可以在浏览器中针对操作的地址发起请求,而返回的数据可以直接显示在浏览器上。下图所示的是通过浏览器调用GetAll操作(http://127.0.0.1:3721/employees/all)得到的结果,我们可以看到所有员工的列表以XML的形式返回。

我们也可以通过浏览器调用Get操作并直接通过在地址中指定员工的ID(http://127.0.0.1:3721/employees/001)并得到以XML表示的基于相应员工的信息。下图所示XML正式ID为001的Employee对象序列化后的结果。如果在请求地址中指定一个不存在的ID(比如http://127.0.0.1:3721/employees/003),由于Get方法中指定了回复状态为NotFound,我们会得到类似于访问资源不存在的错误信息,就像访问一个不存在的Web页面一样。

上面我们演示了通过浏览器以HTTP-GET方式请求操作地址的方式从而直接将返回结果呈现出来,现在我们来演示如何使用通过ChannelFactory<TChannel>创建的服务代理进行服务调用。我们首先在作为客户端应用程序的Client项目中创建一个App.config,并定义如下的配置。

   1: <configuration>
   2:   <system.serviceModel>
   3:     <behaviors>
   4:       <endpointBehaviors>
   5:         <behavior name="webBehavior">
   6:           <webHttp />
   7:         </behavior>
   8:       </endpointBehaviors>      
   9:     </behaviors>
  10:     <client>
  11:       <endpoint name="employeeService"
  12:         address="http://127.0.0.1:3721/employees" 
  13:         behaviorConfiguration="webBehavior"
  14:         binding="webHttpBinding" 
  15:         contract="Artech.WcfServices.Service.Interface.IEmployees"/>
  16:     </client>
  17:   </system.serviceModel>
  18: </configuration>

如上面的配置片断所示,我们定义了一个与服务端相匹配的客户端终结点,该终结点上应用了一个WebHttpBehavior终结点行为。WebHttpBehavior可以说是整个Web HTTP编程模型的核心,绝大部分针对Web的支持都是通过该行为实现的。实际上服务端终结点通过WebServiceHost应用了这个终结点行为。

   1: using(ChannelFactory<IEmployees> channelFactory = new ChannelFactory<IEmployees>("employeeService"))
   2: {
   3:     IEmployees proxy = channelFactory.CreateChannel();
   4:  
   5:     Console.WriteLine("所有员工列表:");
   6:     Array.ForEach<Employee>(proxy.GetAll().ToArray(),employee=>Console.WriteLine(employee));
   7:  
   8:     Console.WriteLine("\n添加一个新员工(003):");
   9:     proxy.Create(new Employee
  10:     {
  11:         Id              = "003",
  12:         Name            = "王五",
  13:         Grade           = "G9",
  14:         Department      = "行政部"
  15:     });
  16:     Array.ForEach<Employee>(proxy.GetAll().ToArray(),employee => Console.WriteLine(employee));
  17:  
  18:     Console.WriteLine("\n修改员工(003)信息:");
  19:     proxy.Update(new Employee
  20:     {
  21:         Id              = "003",
  22:         Name            = "王五",
  23:         Grade           = "G11",
  24:         Department      = "销售部"
  25:     });
  26:     Array.ForEach<Employee>(proxy.GetAll().ToArray(), employee => Console.WriteLine(employee));
  27:     Console.WriteLine("\n删除员工(003)信息:");
  28:  
  29:     proxy.Delete("003");
  30:     Array.ForEach<Employee>(proxy.GetAll().ToArray(), employee => Console.WriteLine(employee));        
  31: }

服务调用程序如上所示,我们模拟了员工的添加、修改和删除。程序之后会在客户端控制台产生如下的输出。

所有员工列表:

   1: 所有员工列表:
   2: ID: 001  姓名: 张三   级别: G7   部门: 开发部
   3: ID: 002  姓名: 李四   级别: G6   部门: 人事部
   4:  
   5: 添加一个新员工(003):
   6: ID: 001  姓名: 张三   级别: G7   部门: 开发部
   7: ID: 002  姓名: 李四   级别: G6   部门: 人事部
   8: ID: 003  姓名: 王五   级别: G9   部门: 行政部
   9:  
  10: 修改员工(003)信息:
  11: ID: 001  姓名: 张三   级别: G7   部门: 开发部
  12: ID: 002  姓名: 李四   级别: G6   部门: 人事部
  13: ID: 003  姓名: 王五   级别: G11  部门: 销售部
  14:  
  15: 删除员工(003)信息:
  16: ID: 001  姓名: 张三   级别: G7   部门: 开发部
  17: ID: 002  姓名: 李四   级别: G6   部门: 人事部

从编程角度来看,我们采用与SOAP服务完全一样的服务调用方式,那么如何反映出服务调用基于Web的本质呢?首先,之前我们能够通过浏览器访问GetAll和Get两个操作可以证明这两个服务操作是基于HTTP-GET的,返回的数据直接以单纯的XML返回,并没有封装成SOAP。为了证明Create、Update和Delete也是完全基于Web的,我们可以通过Fiddler来分析HTTP请求的内容。

如下所示的三段XML片断分别对应着针对上述三个服务操作调用的HTTP请求消息,从这我们可以看出它们就是单纯的针对PUT、POST和DELETE方法的HTTP请求,而传输给服务端的数据直接作为消息的主体,并没有封装成SOAP消息。

   1: Create:
   2: PUT http://jinnan-pc:3721/employees/ HTTP/1.1
   3: Content-Type: application/xml; charset=utf-8
   4: Host: jinnan-pc:3721
   5: Content-Length: 187
   6: Expect: 100-continue
   7: Accept-Encoding: gzip, deflate
   8:  
   9: <Employee xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>销售部</Department><Grade>G11</Grade><Id>003</Id><Name>王五</Name></Employee>
  10:  
  11: Update:
  12: POST http://jinnan-pc:3721/employees/ HTTP/1.1
  13: Content-Type: application/xml; charset=utf-8
  14: Host: jinnan-pc:3721
  15: Content-Length: 186
  16: Expect: 100-continue
  17: Accept-Encoding: gzip, deflate
  18:  
  19: <Employee xmlns="http://www.artech.com/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><Department>行政部</Department><Grade>G9</Grade><Id>003</Id><Name>王五</Name></Employee>
  20:  
  21: Delete:
  22: DELETE http://jinnan-pc:3721/employees/003 HTTP/1.1
  23: Content-Type: application/xml; charset=utf-8
  24: Host: jinnan-pc:3721
  25: Content-Length: 80
  26: Expect: 100-continue
  27: Accept-Encoding: gzip, deflate
(来源于http://www.cnblogs.com/artech/archive/2012/02/04/wcf-rest-sample.html)

认识WCF的更多相关文章

  1. WCF学习之旅—第三个示例之四(三十)

           上接WCF学习之旅—第三个示例之一(二十七)               WCF学习之旅—第三个示例之二(二十八)              WCF学习之旅—第三个示例之三(二十九)   ...

  2. 【WCF】使用“用户名/密码”验证的合理方法

    我不敢说俺的方法是最佳方案,反正这世界上很多东西都是变动的,正像老子所说的——“反(返)者,道之动”.以往看到有些文章中说,为每个客户端安装证书嫌麻烦,就直接采用把用户名和密码塞在SOAP头中发送,然 ...

  3. 【WCF】错误协定声明

    在上一篇烂文中,老周给大伙伴们介绍了 IErrorHandler 接口的使用,今天,老周补充一个错误处理的知识点——错误协定. 错误协定与IErrorHandler接口不同,大伙伴们应该记得,上回我们 ...

  4. 【WCF】自定义错误处理(IErrorHandler接口的用法)

    当被调用的服务操作发生异常时,可以直接把异常的原始内容传回给客户端.在WCF中,服务器传回客户端的异常,通常会使用 FaultException,该异常由这么几个东东组成: 1.Action:在服务调 ...

  5. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  6. 【原创经验分享】WCF之消息队列

    最近都在鼓捣这个WCF,因为看到说WCF比WebService功能要强大许多,另外也看了一些公司的招聘信息,貌似一些中.高级的程序员招聘,都有提及到WCF这一块,所以,自己也关心关心一下,虽然目前工作 ...

  7. Ajax使用WCF实现小票pos机打印源码

    通过ajax跨域方式调用WCF服务,实现小票pos机的打印,源码提供web方式,客户端方式测试,服务驻留右侧底部任务栏,可控制服务开启暂停,用户可自定义小票打印模板,配合零售录入. qq  22945 ...

  8. C# 用SoapUI调试WCF服务接口(WCF中包含用户名密码的验证)

    问题描述: 一般调试wcf程序可以直接建一个单元测试,直接调接口. 但是,这次,我还要测试在接口内的代码中看接收到的用户名密码是否正确,所以,单一的直接调用接口方法行不通, 然后就想办法通过soapU ...

  9. WCF基础

    初入职场,开始接触C#,开始接触WCF,那么从头开始学习吧,边学边补充. SOA Service-Oriented Architecture,面向服务架构,粗粒度.开放式.松耦合的服务结构,将应用程序 ...

  10. Mono下的WCF的Bug?

    最近一段时间,一直在折腾Mono,折腾Linux.让我无比痛苦的是Mono下的WCF的坑真的是太多了,这不又遇到了一个莫名其妙的问题. 环境:mono 3.2.1,Jexus 5.4.3,OS Cen ...

随机推荐

  1. 在 ASP.NET MVC 中使用 HTML Helpers 的那些事

    在 ASP.NET MVC 中使用 HTML Helpers 方法,可以返回得到标准的 HTML 标签,就像 <input>.<button> 或者 <img> 等 ...

  2. kali/centos 更新 java

    kali 转自:http://blog.sina.com.cn/s/blog_5736d8870102w15u.html 墙内的论坛上和博客上有很多这样的文章了,不过一般过程都很复杂,让人看的头晕眼花 ...

  3. 【译】css动画里的steps()用法详解

    原文地址:http://designmodo.com/steps-c... 原文作者:Joni Trythall 我想你在css 动画里使用steps()会和我一样有很多困惑.一开始我不清楚怎样使用它 ...

  4. $.inArray()

    原文链接:http://www.css88.com/jqapi-1.9/jQuery.inArray/ jQuery.inArray( value, array [, fromIndex ] )返回: ...

  5. SSIS学习笔记

    SSIS全称(Sql Server Integration Services),是 Microsoft BI 解决方案的一大利器.除了作为ETL的一种工具,在以下方面还有着突出的表现: (1) 系统维 ...

  6. 不在折腾----hadoop-2.4.1完全分布式集群搭建

    前言 * hadoop2.0已经发布了稳定版本了,增加了很多特性,比如HDFS HA.YARN等.最新的hadoop-2.4.1又增加了YARN HA * 注意:apache提供的hadoop-2.4 ...

  7. TortoiseGit编辑全局变量支持https

    在windows,右键,进入tortoisegit的设置窗口,在左边树形菜单选Git,然后店"编辑全局.gid/config"按钮 输入以下文字 [http] sslVerify ...

  8. [C语言入门笔记]循环与运算符

    循环与运算符 什么是循环? 循环就是一个不停工作的东西,可以反复的实现一个功能,这个才是计算机的重点.计算机可以重复的做一件事情,这样子可以省很多事情 循环的种类有哪些? While Do while ...

  9. 十天精通CSS3学习笔记 part3

    第8章 CSS3中的变形与动画(上) 变形--旋转 rotate() 旋转rotate()函数通过指定的角度参数使元素相对原点进行旋转.它主要在二维空间内进行操作,设置一个角度值,用来指定旋转的幅度. ...

  10. [python] 常用正则表达式爬取网页信息及分析HTML标签总结【转】

    [python] 常用正则表达式爬取网页信息及分析HTML标签总结 转http://blog.csdn.net/Eastmount/article/details/51082253 标签: pytho ...