zoukankan      html  css  js  c++  java
  • WCF揭秘学习笔记(1):基础知识

      最近找工作,面试时经常被问懂不懂WCF。不少招聘高级.NET工程师的要求上都 写着有WCF开发经验的优先考虑。我对于WCF仅仅是通过看一些教学视频这种山寨学习法了解一些。现在要下决心好好学习一下WCF了,所以在网上找了一本 名为《WCF揭秘》的电子书,看目录好像还不错,网上评论一般,但我手头找不到其他WCF电子书籍了,就是它吧。

      言归正传,现在就开始吧!

    WSDL

      WSDL(Web Services Description Language,Web服务描述语言)为将JAVA开发的应用程序与用COM或.NET开的的应用程序一起工作提供了一种通用的解决方案。WSDL提供 了一种使用XML描述软件应用程序接口的方法,实现了WSDL接口的类通常被称为服务。

    面向服务编程

      面向服务编程是将服务(实现了WSDL接口的类)作为可复用类的软件复用方式。它将需要往返传输的数据打包成消息,尽量减少对服务的调用次数。 消息是一种数据传输对象,它包含两个部分:消息头(header)提供与传输数据操作相关的消息;消息体(body)包含传输的数据本身。重要的一点是, 永远不要假设在接收端的消息内容中所读入的对象与在发送端的消息内容中写入的对象是同一类型。所以,消息的发送端与接收端不需要共享同样的类型,也就是它 们在共享数据格式上是松耦合,而非紧耦合。这样,数据的接收端和发送端可以相互独立地设计。

      SOA(service oriented architecture,面向服务架构)是一种通过为所有软件提供服务外观,并将这些服务的WSDL集中发布到一个地方的一种组织企业内的软件的方法。 这个存储WSDL的地方通常是一个UDDI(Universal Description Discovery and Integration,统一描述、发现和集成)注册中心。

      注意,SOA并不是指如何设计那些由面向服务编程开发的组件组成软件的过程。

      面向服务编程一直以来缺乏安全消息传输、失败处理和事务协调标准,推广受到限制。现在这些标准已经有了,而且WCF就提供了实现。

    软件工厂模板

      如果认为WCF只是为面向服务编程的现在和将来提供的一个基础设施,这就太低估它的重要性了。WCF提供了另外一种远比这更加有用的面向服务编程方式,它为软件通信提供了软件工厂模板(software factory template)。

      软件工厂模板的思想是,可以创建一个软件解决方案模型,在检查这个模型并确保它包含了所有的功能性和非功能性需求后,从模型就可生成这个软件了。UML(Unified Modeling Language,统一建模语言)的使用就贯彻了这一思想。

      但使用通过建模语言进行模型驱动软件开发有一个缺点,即通用建模语言本身并不精确。它不能像自然语言那样可表达出需求的小细节,如内存管理、线 程同步等。所以模型驱动开发方式的发展应避开通用建模语言而使用DSL(domain specific language,领域专用语言)。DSL可以为某一特定领域里的概念建模。DSL必须和相应的类框架(即一组专门为该领域设计的类)共同使用。所以,如 果使用DSL为这些类的使用方式建立模型,那就应该可以利用相应的类框架将模型所描述的软件生成出来。

      DSL和相应类框架的结合形成了软件工厂模板的核心。软件工厂模板作为软件工厂提供软件产品提供了保证,通过它,同类软件产品的各种变体可以很快生产出来。

      如:Microsoft Visual Studio .NET及后续版本中的Windows Forms Designer就是软件工厂模板的一个良好例子。Windows Forms Designer就是DSL,.NET Framework中的System.Windows.Forms命名空间中的类便是类框架。Windows Forms Designer的用户就是利用它从那些类中生成的软件建模的。

      WCF是软件通信的软件工厂模板,它包含了称为服务模型(service model)的DSL和一个称为信道层(channel layer)的类框架。服务模型由System.ServiceModel命名空间中的类和一种XML配置语言组成。信道层包含了 System.ServiceModel.Channel命名空间中的类。开发者使用服务模型为软件如何通信建模,然后根据他们的模型从信道层生成需要加 入到软件中的通信组件。如果需要改变或增加软件通信的功能,只需修改模型,就能生成对软件的修改或增加。如果希望为一种现有信道层不支持的通信方式建模, 可以自己创建一个或购买一个合适的信道加入到信道层,然后就像平常一样继续生成他们的软件。这与Windows Forms Designer是一样的,使用者可以自己编写或购买控件加入到设计器的工具箱里。

      总而言之,WCF提供的软件工厂模板能从模型中生成、修改并补充软件通信工具,这具有非凡的意义。

    服务模型

      WCF服务模型语言的主要术语和WSDL的主要术语有紧密的对应关系。在WSDL中,对应于网络上通信的软件称为服务。服务通过XML文档进行描述,这些文档主要由三个部分组成:

      1. 服务部分说明服务位于何处。

      2. 绑定部分说明服务所能理解的标准通信协议。

      3. portType部分列出服务所能提供的所有操作,它定义了服务根据接收的消息所发出的响应消息。

      这样,WSDL的三个主要部分能告诉你服务的位置、如何与之通信,及它能做什么。

      这三个部分恰好正是使用WCF服务模型服务时所需确定的:服务的位置、如何与之通信及它能做什么。在WSDL中分别称它们为服务、绑定和 portType,在WCF模型中它们分别被称为地址(address)、绑定(binding)、契约(contract)。所以,a、b、c可作为 WCF服务模型的三个主要术语的缩写。

      更确切地说,在WCF服务模型中,对应通信功能的软件称为服务。服务具有一个或多个终结点(endpoint),通信会被定向到这些终结点。终 结点由地址、绑定和契约组成。在终结点定义外部接口后,服务在内部如何处理通信是由一组称为行为(behavior)的控件点(control point)所决定的。

      开发者首先从定义契约开始,这个步骤只需编写一个接口,并为接口和其一个或多个方法添加一些服务模型提供的特性,将接口指定为WCF的契约:

    [ServiceContract]
    publicinterface IEcho
    {
    [OperationContract]
    string Echo(string input);
    }

      下一步是契约的实现:

    复制代码
    [ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Multiple)]
    publicclass Service : IEcho
    {
    publicstring Echo(string input)
    {
    return input;
    }
    }
    复制代码

      一个类如果实现了被指定为WCF契约的接口,它就称为服务类型(service type)。开发者应通过ServiceBehavior特性告诉WCF如何把从终结点接收到的数据传送给服务类型。上例中,该特性表示WCF传送数据给 服务类型时是否可以使用多条并发线程。

      开发者的最后一步即是将服务承载(host)在一个应用程序域(application domain)中。IIS和其他任何.NET程序都可以为承载服务提供应用程序域。只要使用WCF模型提供的ServiceHost类,就可很容易地将服 务承载在任意的.NET程序中:

    复制代码
    using(ServiceHost host =new ServiceHost(typeof(Service))
    {
    host.Open();

    Console.WriteLine(
    "The service is ready.");
    Console.ReadKey(
    true);
    host.Close();
    }
    复制代码

      下一步应该管理员接手了。管理员通过将一个地址和绑定与服务类型所实现的WCF契约相关联,为服务类型定义一个终结点。WCF提供了一个称为Service Configuration Editor(服务配置编辑器)的编辑工具来完成这个任务。

      再下来,终结点的地址、绑定和契约都定义好,契约也实现了,服务的宿主环境也已提供,服务就可以使用了。在管理员运行宿主程序后,WCF会检查服务模型语言所定义的终结点地址、绑定、契约及行为,然后生成必要的组件以便服务可以接收和响应来自信道层的通信。

      管理员可以控制服务是否向外发部描述其终结点的WSDL,如果选择发布,WCF会自动生成相应的WSDL并向需要它的请求返回该WSDL。开发人员可下载相应的WSDL,然后生成能与服务交换数据的代码及描述终结点信息的应用配置文件。这些操作只需一条命令即可完成:

      svcutil http://localhost:8000/EchoService?wsdl

      其中,svcutil是WCF的服务模型元数据工具(Service Model Metadata Tool),http://localhost:8000/EchoService?wsdl是服务元数据的下载地址。在使用该工具产生必要的代码和配置信息后,开发者就可利用它们和服务通信了。

    using(EchoProxy echoProxy =new EchoProxy())
    {
    echoProxy.Open();
    string response = echoProxy.Echo("Hello, World!");
    echoProxy.Close();
    }

      前面给出的这些步骤就是使用WCF所需的所有步骤。总结一下,这些简单的步骤如下:

      1. 服务开发者使用.NET接口定义一份契约。

      2. 服务开发者编写一个实现该接口的类,即服务类型。

      3. 服务开发者通过给服务类型及其方法添加特性WCF行为做出适当的修改。

      4. 服务开发者为服务提供恰当的运行宿主环境。如果服务需要承载在一个.NET程序中,开发者应先开发此程序。

      5. 服务管理员使用Service Configuration Editor配置服务的终结点,即为已被服务类型实现的契约关联地址和绑定。

      6. 服务管理员使用Service Configuration Editor对WCF行为做出恰当的修改。

      7. 客户程序的开发者使用服务模型元数据工具下载描述服务的WSDL并生成与服务通信所需的代码和配置文件。

      8. 客户程序的开发者利用生成的代码和配置文件与服务通信。

      注意:服务类型不只是实现了服务契约接口的类,那些直接添加了ServiceContract特性的类也是服务类型。然而,先为接口添加 ServiceContract特性,然后用类实现接口的方法,能生成一个可被任意多服务类型实现的服务契约。这样,可根据需要将一个实现了服务契约的服 务类型用另外一个替换。

    为终结点指定地址和绑定

      为终结点指定地址和绑定并不需要编写任何代码,实现上,这一般是管理员而不是程序员的工作。虽然也可以在代码里指定地址和绑定,但如果这样做, 为更改地址和绑定就需要修改代码了。WCF的一个重要创新就是将如何开发软件与软件如何通信分开。软件如何通信是由绑定确定的。所以,一般情况下,我们应 该避免在代码中指定终结点的地址和绑定,而应该通过配置宿主应用程序来指定。

    手动方式指定地址和绑定

      为宿主程序添加程序配置文件app.config,该文件内容示例如下:

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    <system.serviceModel>
    <services>
    <!-- 指定该配置文件所配置的宿主程序承载的服务类型 -->
    <service name="DerivativesCalculator.DerivativesCalculatorServiceType">
    <host>
    <baseAddresses>
    <!-- 使用URI格式指定服务宿主的基地址 -->
    <add baseAddress="http://localhost:8000/Derivatives/"/>
    <add baseAddress="net.tcp://localhost:8010/Derivatives/"/>
    </baseAddresses>
    </host>
    <!-- 为服务类型功能提供一个终结点,包括地址、绑定、契约 -->
    <endpoint address="Calculator" binding="basicHttpBinding"
    contract
    ="DerivativesCalculator.IDerivativesCalculatorServiceType"/>
    </service>
    </services>
    </system.serviceModel>
    </configuration>
    复制代码

      注意:

      1. 为服务终结点设定的地址是基地址的相对地址。URI第一个“/”前的部分称为方案(scheme),提供给WCF服务的每个基地址必须使用不同的策略。

      2. 终结点的绑定由binding="basicHttpBinding"确定。WCF绑定定义了与服务通信的协议组合。每个协议由一个绑定元素(binding element)表示,而每个绑定都由一组绑定元素组成。WCF的信道层主要提供的就是绑定元素。

      一类特定绑定元素实现了传送消息的协议,其中之一便是实现了HTTP的绑定元素,另一个实现了TCP协议。

      另外一类特殊的绑定元素定义了消息编码的协议。WCF提供了三个这类绑定元素:一种是将SOAP消息编码成文本;另一种将SOAP消息编码成二 进制格式;第三种将SOAP消息根据SOAP而MTOM(Message Transmission Optimization Mechanism,消息传输优化机制)编码。MTOM编码适合那些包含大量二进制数据的SOAP消息。

      既不是传输协议绑定元素也不是消息编码绑定元素的例子有实现WS-Security协议和WS-ReliableMessaging协议的绑定 元素。WCF中可扩展性很重要的一个方面就是任何软件开发者都可提供新的绑定元素。WCF绑定就是一组绑定元素,它们至少包含一个传输绑定元素及数量不限 的其他绑定元素。如果没有指定消息编码绑定元素,那么传输协议绑定元素将采用默认的消息编码协议。

      绑定可以在代码或配置文件中通过逐一选择绑定元素来定义,WCF也提供了几个包含常用绑定元素组合的类,这些类被称为预定义绑定。 BasicHttpBinding是预定义的绑定之一,它代表了HTTP传输绑定元素与将SOAP消息编码成文本格式的绑定元素的组合。 BasicHttpBinding根据WS-I Basic Profile Specification 1.1配置这些绑定元素配置。后者是一组挑选取出来的Web服务规范,其目的是提高各种平台上的Web服务及用户间的互操作性。

      全部现有的预定义绑定见下表,它们都直接或间接继承自类System.ServiceModel.Channels.Binding。

      

      在上例中,终结点的地址被设为Calculator,这个地址是基地址的相对地址。那这个终结点的基地址是之前定义的哪一个呢?这是由基地址的策略和传输绑定元素实现的传输协议来决定的。下表列出了它们的对应关系:

    部署服务

      由于安全因素,默认在IE中访问http基地址显示的页面中,元数据发布功能被禁止了。这样,除非开发者或管理员明确设定了需要显示的内容,其他任何有关服务的信息都不会暴露。要发布元数据,我们可更改配置文件:

    复制代码
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
    ......
    <service name="DerivativesCalculator.DerivativesCalculatorServiceType"
    behaviorConfiguration
    ="DerivativesCalculatorService">
    </service>
    <behaviors>
    <serviceBehaviors>
    <behavior name="DerivativesCalculatorService">
    <serviceMetadata httpGetEnabled="true"/>
    </behavior>
    </serviceBehaviors>
    </behaviors>
    ......
    </configuration>
    复制代码

      上面更改的实质就是让服务在响应HTTP GET的访问时,生成它自己的元数据。

      在浏览器中的URI后加上wsdl查询,如http://localhost:8000/Derivatives/?wsdl,这时服务的WSDL就可显示了。

    使用服务

      我们可使用svcutil工具来生成WCF服务的客户端代码类及相关配置文件,如:

      svcutil http://localhost:8000/Derivatives/ /out:Client.cs /config:app.config

      该命令在运行WCF的服务元数据工具时,将服务使用的http策略的基地址作为命令参数。如果该地址使用http策略,且该服务已启用了通过 HTTP GET发布元数据功能,服务元数据工具便能获取到该服务的WSDL和其他相关元数据。默认情况下,它会创建一个与服务通信的代码类,且生成一个包含服务终 结点定义的.NET程序配置文件。其代码类的使用见下例:

    复制代码
    publicstaticvoid Main(string[] args)
    {
    decimal result =0;
    using(DerivativesCalculatorProxy proxy =new DerivativesCalculatorProxy("BasicHttpBinding_IDerivativesCalculator))
    {
    proxy.Open();
    result
    = proxy.CalculateDerivative(newstring[]{"MSFT"}, newdecimal[]{ 3 }, newstring[]{} };
    proxy.Close();
    }
    }
    复制代码

      添加到Client项目的app.config文件必须有这个终结点的定义,包括地址、绑定和契约:

    复制代码
    <client>
    <endpoint
    address="http://localhost:8000/Derivatives/Calculator"
    binding
    ="basicHttpBinding"
    bindingConfiguration
    ="BasicHttpBinding_IDerivativesCalculator"
    contract
    ="IDerivativesCalculator"
    name
    ="BasicHttpBinding_IDerivativesCalculator"/>
    </client>
    复制代码

      注意,终结点定义中的名称与传给代理类构造函数的参数是一致的。

      这个终结点配置中应用的名称为BasicHttpBinding_IDerivativesCalculator的绑定配置实际上给出了预定义的BasicHttpBinding的属性默认值。

      我们也可以自己写一个代理类,而不需使用Windows服务元数据工具来生成。这只需定义一个从WCF的ClientBase<T>泛型继承的类,且实现服务的契约即可。

    复制代码
    [ServiceContract]
    public interfact IDerivativesCalculator
    {
    [OperationContract]
    decimal CalculateDerivative(string[] symbols, decimal[] parameters, string[] functions);
    ...
    }

    publicpartialclass DerivativesCalculatorProxy : ClientBase<IDerivativesCalculator>, IDerivativesCalculator
    {
    public decimal CalculateDerivative(string[] symbols, decimal[] parameters, string[] functions)
    {
    return base.Channel.CalculateDerivative(symbols, parameters, functions);
    }
    }
    复制代码

      另外一种编写服务客户端的方法是给定一个契约的定义:

    IDerivativesCalculator proxy =new ChannelFactory<IDerivativesCalculator>("BasicHttpBinding_IDerivativesCalculator").CreateChannel();
    proxy.CalculateDerivative(...);
    ((IChannel)proxy).Close();

      ChannelFactory<T>泛型是在System.ServiceModel.Channels命名空间里定义的。第一个 编写客户端的方法产生一个可复用的代理类,而第二个方法只是生成了一个代理变量。第三种方法是使用MetadataExchangeClient类自己下 载服务的元数据,然后在执行时根据元数据相应的配置代理。

    在IIS中承载WCF服务

      只有那些所有终结点都具有HTTP绑定的服务才可承载在IIS 5.1或IIS 6中。IIS 7中添加了一个新功能叫Windows激活服务(Windows Activation Service),它可将使用任何传输协议的消息转发给.NET程序集。这样,不管WCF服务使用何种传输协议都可在IIS 7中承载

    http://www.cnblogs.com/free722/archive/2011/05/22/2053181.html

  • 相关阅读:
    功能:Java注解的介绍和反射使用
    功能:@Vaild注解使用及扩展
    转载:微信小程序view布局
    功能:Java8新特性steam流
    功能:Linux运行jar包Shell脚本
    转载:Windows使用tail -f 监控文件
    转载:java.math.BigDecimal 比较大小
    问题:跨域及解决方案
    基于 @SelectProvider 注解实现无侵入的通用Dao
    SpringBoot中的异步操作与线程池
  • 原文地址:https://www.cnblogs.com/mili3/p/3737493.html
Copyright © 2011-2022 走看看