老老实实学WCF

第二篇 配置WCF

在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它。先回顾一下服务端的代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.ServiceModel;
  6. using System.ServiceModel.Description;
  7. namespace HelloWCFService
  8. {
  9. class Program
  10. {
  11. static void Main(string[] args)
  12. {
  13. Uri baseAddress = new Uri("http://localhost:8000/MyService");
  14. ServiceHost host = new ServiceHost(typeof(HelloWCFService), baseAddress);
  15. host.AddServiceEndpoint(typeof(IHelloWCFService), new WSHttpBinding(), "HelloWCFService");
  16. ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
  17. smb.HttpGetEnabled = true;
  18. host.Description.Behaviors.Add(smb);
  19. host.Open();
  20. Console.WriteLine("Service is Ready");
  21. Console.WriteLine("Press Any Key to Terminate...");
  22. Console.ReadLine();
  23. host.Close();
  24. }
  25. }
  26. [ServiceContract]
  27. interface IHelloWCFService
  28. {
  29. [OperationContract]
  30. string HelloWCF();
  31. }
  32. public class HelloWCFService : IHelloWCFService
  33. {
  34. public string HelloWCF()
  35. {
  36. return "Hello WCF!";
  37. }
  38. }
  39. }

所有的这些代码都写在program.cs中,干净清爽。

我们稍微审视一下这段程序会发现,我们用了很多的代码来定制服务的特性,例如基地址、终结点、绑定、行为等。这些都叫做配置。而真正对服务的本身的定义是很少的(主逻辑就是返回一个字符串而已),因此我们不难看出,WCF的编程中配置占了很大的比重。

WCF的配置选项是很多的,我们这里只考虑最简单的情况。我们在定义和实现了服务协定后,至少应该做哪些配置才能让服务运行起来呢?

(1) 依据服务实现类配置一个服务(ServiceHost)。

(2) 指定一个基地址(如果终结点中指定了绝对地址,这步可以省略)。

(3) 建立一个终结点,并为其指定地址、绑定和服务协定。

(4) 建立一个元数据交换终结点。

(5) 为服务添加一个行为来启用元数据交换。

虽然在.Net 4.0下微软提供了简化配置,我们甚至可以一行配置都不做,但是为了搞清楚配置的基本原理,我们暂时不考虑简化配置的情况。

以下这些配置是我们必须要做的,我们从代码中可以看到以上几种配置相应语句:

建立基地址:

  1. Uri baseAddress = new Uri("http://localhost:8000/MyService");

建立服务:

  1. ServiceHost host = new ServiceHost(typeof(HelloWCFService), baseAddress);

建立终结点并指定地址、绑定和服务协定:

  1. host.AddServiceEndpoint(typeof(IHelloWCFService), new WSHttpBinding(), "HelloWCFService");

添加元数据交换终结点并添加启用元数据交换行为

  1. ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
  2. smb.HttpGetEnabled = true;
  3. host.Description.Behaviors.Add(smb);

看上去清楚明白,但是只是看上去很美,这样的配置方式存在弊端,例如基地址,如果当服务部署之后迁移了服务器,基地址发生变化,我们必须修改源程序并重新编译重新部署才能实现这个要求。对于其他的配置选项亦是如此。这对于产品环境是不能接受的。好在WCF提供针对这个问题的解决方案:配置文件。

我们把对服务的配置写在应用程序的配置文件中(IIS程序是web.config 其他程序是app.config),当配置发生改变的时候我们就不用重新编译程序集了。

配置文件的写法很复杂,有很多选项,为了便于上手,我们先从跟本例相关的选项开始。

在配置文件中,根节是<configuration>,所有的配置元素都位于其中。对于WCF服务的配置部分,最外层的节是<system.serviceModel>,所以配置文件中至少先应该是这个样子:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. </system.serviceModel>
  5. </configuration>

现在我们准备开始配置一个服务,服务配置元素标签为<services></services>,是<system.serviceModel>的子节,在一个宿主上可以承载许多服务,每一个服务用<service></service>来配置,它是<services>的子节。在配置<service>前,我们还要先添加一个基地址配置,基地址用<baseaddress>描述,他是<host>的子节,<host>是<service>的子节。

晕了么...慢慢来。

先把<services>节加上,这里可以容纳许多服务,注意这个是带s的

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. </services>
  6. </system.serviceModel>
  7. </configuration>

在<services>的怀抱中,我们添加一个<service>,这是我们要配置的服务本体,注意这个是不带s的

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service>
  6. </service>
  7. </services>
  8. </system.serviceModel>
  9. </configuration>

在<service>中,添加一个基地址,先添加一个<host>再添加一个<baseaddress>

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service>
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. </service>
  12. </services>
  13. </system.serviceModel>
  14. </configuration>

到这里,基地址的部分已经完成,对应代码中的位置你找到了么?

服务的配置还差一点,我们在代码中为服务指定了实现类型,在配置文件中如何指定呢?就用<service>标签的name属性,指定的时候后要用完全限定名(带着命名空间)

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service name="HelloWCFService.HelloWCFService">
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. </service>
  12. </services>
  13. </system.serviceModel>
  14. </configuration>

我这个例子举的不好了,命名空间和实现类型是一个名字,要注意区分。

到这里,服务也配置完了,对应代码的位置翻上去找一下。

接下来配置终结点,终结点用<endpoint>元素表示,正如代码实现中的参数,在配置中也需要一一指定地址、绑定和服务协定接口,分别用address、binding和contract属性来表示,当然<endpoint>也是<service>的子节,毕竟他是属于服务的嘛。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service name="HelloWCFService.HelloWCFService">
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. <endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" />
  12. </service>
  13. </services>
  14. </system.serviceModel>
  15. </configuration>

这里用了相对地址"HelloWCFService",他会和基地址组合在一起(排在后面)成为终结点的地址,这里也可以指定为空字符串"",此时基地址(即服务地址)就是终结点的地址,还可以指定一个绝对地址,那样他会覆盖基地址,基地址对这个终结点来说就不起作用了,这里可以看出,终结点的地址是独立的,可以是基地址的子地址,也可以独立使用另一个地址,他们之间没有必然的链接。

这里的contract 同<service>里面一样,也要使用完全限定名称(带上命名空间)。

注意,在使用IIS承载的时候,必须使用相对地址,也就是终结点必须是基地址的子地址,这是由IIS的部署结构决定的。

到这里终结点也配置完成,对应代码的位置找到了吗?

接下来是最后一步(或者说两步),配置元数据交换终结点并开启元数据交换行为。这个过程,代码中用了三行,实际上代码这三行仅仅是添加了元数据交换行为,并没有配置元数据交换终结点,运行时系统为我们自动添加了终结点。这两点缺一不可,虽然系统会为我们添加,我们还是要知道这个配置的写法。这个很重要。

开启元数据交换从原理上应该是两件事,第一是服务允许元数据交换,第二是服务提供元数据交换方式,第一条就是添加元数据交换行为,表示服务允许这样的请求,第二条就是服务告诉你如何请求,客户端是通过一个固定的终结点去请求的,这个终结点的地址、绑定和协定都是固定的,我们不能更改,这个是框架的约定,我们只能按要求配置。

首先,第一条,允许元数据交换,这个是通过为服务添加一个行为来实现的,行为是用来描述服务的特性的,不同的服务可能有相同的行为,行为并不是归某服务独有的,因此行为被定义为<system.serviceModel>节的子节,可以被不同的服务引用,他的定义有些像服务,外面是带s的,里面是不带s的,毕竟可能有许多的行为定义嘛。

先定义个行为:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service name="HelloWCFService.HelloWCFService">
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. <endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" />
  12. </service>
  13. </services>
  14. <behaviors>
  15. <serviceBehaviors>
  16. <behavior name="metaExchange">
  17. <serviceMetadata httpGetEnabled="true"/>
  18. </behavior>
  19. </serviceBehaviors>
  20. </behaviors>
  21. </system.serviceModel>
  22. </configuration>

因为存在服务行为和终结点行为之分,所有<behaviors>和<behavior>之间又套了一个<serviceBehaviors>,表示这个是服务行为,我们为行为制定了名字,好让<service>可以引用,也可以不指定,那么所有服务都会应用。交换元数据的行为有固定的标签描述,就是<serviceMetadata>,对着代码看,很熟悉吧。

建立了行为以后,要让我们刚才建立的服务去引用他,用<service>的behaviorConfiguration属性:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service name="HelloWCFService.HelloWCFService"behaviorConfiguration="metaExchange">
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. <endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" />
  12. </service>
  13. </services>
  14. <behaviors>
  15. <serviceBehaviors>
  16. <behavior name="metaExchange">
  17. <serviceMetadata httpGetEnabled="true"/>
  18. </behavior>
  19. </serviceBehaviors>
  20. </behaviors>
  21. </system.serviceModel>
  22. </configuration>

接下来第二步,建立元数据交换终结点,建立的位置和我们刚才建立的终结点位置相同,但是属性是固定的,大小写都不能写错。

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3. <system.serviceModel>
  4. <services>
  5. <service name="HelloWCFService.HelloWCFService" behaviorConfiguration="metaExchange">
  6. <host>
  7. <baseAddresses>
  8. <add baseAddress="http://localhost:8000/MyService"/>
  9. </baseAddresses>
  10. </host>
  11. <endpoint address="HelloWCFService" binding="wsHttpBinding" contract="HelloWCFService.IHelloWCFService" />
  12. <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  13. </service>
  14. </services>
  15. <behaviors>
  16. <serviceBehaviors>
  17. <behavior name="metaExchange">
  18. <serviceMetadata httpGetEnabled="true"/>
  19. </behavior>
  20. </serviceBehaviors>
  21. </behaviors>
  22. </system.serviceModel>
  23. </configuration>

到这里,配置文件就写完了。我们把它放到程序里面去,打开上一篇中建立的服务端程序,为程序添加一个配置文件

右键点击项目->添加->新建项->应用程序配置文件,名字系统会起好(app.config)。把上面的配置写进去,保存。

既然我们已经有了配置文件,就不需要(也不应该)在代码中配置了。代码中的配置会覆盖掉配置文件中的配置。所以我们要对代码修改一下。

main函数中只留下这么几行:

  1. ServiceHost host = new ServiceHost(typeof(HelloWCFService));
  2. host.Open();
  3. Console.WriteLine("Service is Ready");
  4. Console.WriteLine("Press Any Key to Terminate...");
  5. Console.ReadLine();
  6. host.Close();

其中,建立SeviceHost 那行被修改了,去掉了baseAddress的参数,但是我们仍需要告诉host 我们要寄存的服务类的类型。

F5运行起来。

然后在浏览器中访问一下服务试试

  1. http://localhost:8000/MyService

是不是和昨天的结果一样呢。

(CSDN的传图好象挂了哩...)

总结一下今天的学习。

我们使用配置文件的方法完成了对WCF服务的配置,从中接触到了服务、终结点和行为的配置方法。配置文件的元素还有许多,像绑定、安全性等等特性。在今后学到的时候再慢慢展开,配置文件的每一个元素都应该力求背着写下来,一行一行的写,在写的过程中体会,而不是四处复制和粘贴,这样才能对配置文件的写法有深刻的印象。

相关资源

徐长龙老师播讲的《跟我一起从零开始学WCF系列课程》

http://msdn.microsoft.com/zh-cn/hh148206

MSDN技术资源库中的WCF参考

http://msdn.microsoft.com/zh-cn/library/dd456779.aspx

第二篇 配置wcf的更多相关文章

  1. [老老实实学WCF] 第二篇 配置WCF

    老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...

  2. 老老实实学习WCF[第二篇] 配置wcf

    老老实实学WCF 第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Col ...

  3. (转)[老老实实学WCF] 第二篇 配置WCF

    第二篇 配置WCF 在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Collections. ...

  4. (转)[老老实实学WCF] 第二篇 配置WCF

    在上一篇中,我们在一个控制台应用程序中编写了一个简单的WCF服务并承载了它.先回顾一下服务端的代码: using System; using System.Collections.Generic; u ...

  5. WCF学习第二篇:WCF 配置架构。这有助于对wcf配置的理解和记忆

    使用 Windows Communication Foundation (WCF) 配置元素,您可以配置 WCF 服务和客户端应用程序. 可以使用配置编辑器工具 (SvcConfigEditor.ex ...

  6. 跟我学PHP第二篇- 配置Mysql以及PHP WampServer篇(1)

    大家好,昨天我给大家介绍了如何去安装ZEND STUDIO,下面昨天文章的链接: http://www.cnblogs.com/kmsfan/p/zendStudio.html 本节为配置的第一部分, ...

  7. 小白学Maven第二篇配置Ecilpse

    Maven:里面提到了一个很重要的概念:中央仓库,本地仓库,私服: 中央仓库:是Maven通过一个地址索引去(http://mvnrepository.com/)下载需要的架包: 本地仓库:是Mave ...

  8. SpringBoot之旅第二篇-配置

    一.引言 虽然springboot帮我们进行了自动配置,但配置还是不可避免的,比如最简单的端口号,数据库连接.但springboot的配置一般不用xml进行配置,而是yml和properties,选择 ...

  9. elasticsearch 第二篇(配置篇)

    配置 在es启动之前可以通过设置启动命令行启动参数.环境变量.文件等方式优化和配置es进行参数 环境变量 名称 示例 说明 ES_MIN_MEM 256M 用于配置java进程分配的最小内存 ES_M ...

随机推荐

  1. pypcap rpm制作

    1.下载地址 https://pypi.org/project/pypcap/#history 2.下载后,解压并制作rpm tar -xvf pypcap-1.2.3.tar.gz python s ...

  2. 牛客网PAT练兵场-A除B

    题目地址:https://www.nowcoder.com/pat/6/problem/4043 题解:遍历大数,边除边输出,最后得到余数输出即可 /** * *作者:Ycute *时间:2019-1 ...

  3. Hadoop 2.6.1 集群安装配置教程

    集群环境: 192.168.56.10 master 192.168.56.11 slave1 192.168.56.12 slave2 下载安装包/拷贝安装包 # 存放路径: cd /usr/loc ...

  4. java父类子类代码

    import java.util.Scanner;import java.util.*; class PersonF{ public void print(String ID,String Workc ...

  5. Qt 怎样生成带图标的exe

    一.问题描述 当我们在 Windows 下用 VS 生成 exe 程序时,如果窗口程序指定了图标,那么生成的 exe 程序便是指定的图标模样. 但是,当使用 Qt Creator 编译程序却不同.即使 ...

  6. Laravel 避免 Trying to get property of non-object 错误的六种方法 [新增第六种 data_get]

    在使用链式操作的时候,例如: return $user->avatar->url;如果 $user->avatar 为 null,就会引起 (E_ERROR) Trying to g ...

  7. 攻防世界——Misc新手练习区解题总结<3>(9-10题)

    第九题SimpleRAR: 下载附件后得到一个压缩包打开后得到如下提示 文件头损坏,让我们打开winhex看一下 7a为子块而文件头为74,这里将7a改为74(这里我也不是很清楚,详细大家可以自行去查 ...

  8. android MVVM(2)用数据绑定关联VM 与 V

    1.官方文档 https://developer.android.com/topic/libraries/data-binding/architecture 2.简介 数据绑定库 可与MVVM 架构组 ...

  9. javaScript 推荐书籍 由简单到入门,初级到高级。

  10. ElasticSearch7.6.1 概述

    本来打算重新回去看 并发编程的,之前看过一遍,现在基本忘完了,然后因为考虑到项目的需要,就先看ES了 然后再B站上看到一个视屏比较火,就看这个吧 给大家推荐一下 https://www.bilibil ...