1. 然后我们在<Client>中添加一个终结点,这个是客户端的终结点,我们前面曾经提过,通信实际上发生在两个终结点间,客户端也有个终结点,然而请求总是从客户端首先发起,所以终结点地址应该填写为服务端终结点的地址让客户端来寻址,但是服务协定要客户端本地是有的,所以这个要写本地定义的那个协定的完全限定名:

引用http://blog.csdn.net/songyefei/article/details/7389186

第五篇 再探通信--ClientBase

在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创建了通道,实现了和服务端的通信。然而,与服务端通信的编程模型不只一种,今天我们来学习利用另外一个服务类ClientBase<>来完成同样的工作,了解了这个类的使用方法,我们对服务引用中的关键部分就能够理解了。

ClientBase<>类也是一个泛型类,接受服务协定作为泛型参数,与ChannelFactory<>不同的是,这个类是一个基类,即抽象类,是不能实例化成对象直接使用的,我们需要自己写一个类来继承这个类,我们新写的类实例化出来就是客户端代理了,这个对象可以调用基类的一些受保护的方法来实现通信。ClientBase<>为我们封装的很好,我们只需要写个新类来继承他就可以了,通信的很多部分他都替我们做好了,比如我们不用进行去创建通道和打开通道的操作,直接调用协定方法就可以了。这也是服务引用和其他元数据生成工具(如svcutil)使用这个类来构造客户端代理的原因。

我们一边动手一边学习

1. 建立客户端程序

我们依然不用服务引用,完全手写,这次还是建立一个控制台应用程序作为客户端。

2. 添加必要的引用

我们用到的ClientBase<>类在System.ServiceModel程序集中,因此,要添加这个程序集的引用,同时在program.cs中添加using语句

    1. using System.ServiceModel;

这一步应该很熟了,如果有疑问,返回前几篇温习一下。

因为没有使用服务引用,客户端现在没有任何元数据的信息,我们要来手写。首先把服务协定写进去。这个再熟悉不过了(写在Program类后面):

  1. [ServiceContract]
  2. public interface IHelloWCF
  3. {
  4. [OperationContract]
  5. string HelloWCF();
  6. }

4. 编写客户端代理类

上面已经提到,我们要自己写一个新类来继承ClientBase<>基类,这样这个新类就是代理类了,同时,为了能够用代理类直接调用服务协定的方法,我们还要让代理类实现服务协定的接口,注意,继承要写在前面,实现接口要写在后面。我们把这个类起名为HelloWCFClient。

  1. public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
  2. {
  3.  
  4. }

ClientBase<>有许多的构造函数,接受不同种类的参数来创建代理类对象,其实这些参数都是元数据信息,刚才我们已经通过泛型参数传递给基类服务协定这个元数据了,现在基类还需要绑定和终结点地址这两个元数据才能正确创建连接,所以我们继承的新类应该把这个构造函数给覆载一下接受这两种元数据参数并传递给基类。

  1. public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
  2. {
  3. public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
  4. : base(binding, remoteAddress)
  5. {
  6.  
  7. }
  8. }

我们看到这个新建的构造函数什么也没做,只是接受了两个参数,一个是绑定,一个是终结点地址,然后直接调用基类(也就是ClientBase<>)的构造函数,把这个两个参数传递了上去。其实工作都是ClientBase<>做的,我们新建的类就是个传话的,要不然怎么叫代理呢,他什么活都不干。

既然我们实现了服务协定接口,当然要实现接口的方法了。下面我们把方法的实现写下来:

  1. public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
  2. {
  3. public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
  4. : base(binding, remoteAddress)
  5. {
  6.  
  7. }
  8.  
  9. public string HelloWCF()
  10. {
  11. return base.Channel.HelloWCF();
  12. }
  13. }

别忘了你的处境,凡人!我们这是在客户端啊,怎么可能有服务协定呢?这个可以有,但是这个实现不是我们在做,而是要和服务端通信让服务端做,这里可以看到代理鲜明的特点了,代理类虽然实现了服务协定的方法,但是在方法中,他调用了基类(就是ClientBase<>)上的通道,并通过通道调用了了协定方法。此时,ClientBase<>已经为我们建立好与服务端的通道了,而且是用服务协定建立的,我们当然可以在通道上调用服务协定的方法。所以调用代理类对象的HelloWCF()的过程是代理类委托基类在已经建立好的服务协定通道上调用协定方法,并从服务端获得返回值,然后再返回给代理类对象的调用者。狗腿啊狗腿。

5. 编写程序主体

代理类已经写完了,我们现在开始写程序的主体,让我们来到Program的Main函数中。

还是有一些准备要做,还差两个元数据呢,对了,就是绑定和地址。和上一篇一样,我们先建立这个两个元数据的对象备用:

  1. WSHttpBinding binding = new WSHttpBinding();
  2. EndpointAddress remoteAddress = new EndpointAddress("http://localhost/IISService/HelloWCFService.svc");

接下来就要建立我们的代理类对象了,new一个出来吧:

  1. HelloWCFClient client = new HelloWCFClient(binding, remoteAddress);

把我们刚刚建立的两个元数据对象作为参数传递进去。

接下来就可以调用服务协定方法了:

  1. string result = client.HelloWCF();

别忘了关闭通道,ClientBase<>很贴心的为我们准备了这个方法,不用再做强制类型转换什么的了(见前一篇)。

client.Close();

以下是Program.cs的全部代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. using System.ServiceModel;
  7.  
  8. namespace ConsoleClient
  9. {
  10. class Program
  11. {
  12. static void Main(string[] args)
  13. {
  14. WSHttpBinding binding = new WSHttpBinding();
  15. EndpointAddress remoteAddress = new EndpointAddress("http://localhost/IISService/HelloWCFService.svc");
  16.  
  17. HelloWCFClient client = new HelloWCFClient(binding, remoteAddress);
  18.  
  19. string result = client.HelloWCF();
  20.  
  21. client.Close();
  22.  
  23. Console.WriteLine(result);
  24. Console.ReadLine();
  25. }
  26. }
  27.  
  28. [ServiceContract]
  29. public interface IHelloWCF
  30. {
  31. [OperationContract]
  32. string HelloWCF();
  33. }
  34.  
  35. public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
  36. {
  37. public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
  38. : base(binding, remoteAddress)
  39. {
  40.  
  41. }
  42.  
  43. public string HelloWCF()
  44. {
  45. return base.Channel.HelloWCF();
  46. }
  47. }
  48. }

6. 再展开一点点

这样看上去已经挺成功了,我们现在再打开服务引用的reference.cs代码,看看是不是大部分都看得懂了?监管有些地方他写的可能有些复杂,比如描述协定的属性参数啊,代理类更多的构造函数啊,但是核心的就是我们刚刚写的部分,那一大堆wsdl什么的其实都不是核心,不信的话你把他们都删掉,就留一个reference.cs看看还好不好用?那一堆东西也不是没用就是现在自己看起来还蛮复杂的,我们到后面一点点学习。

我们仔细看服务引用的reference.cs代码,有一样东西是我们有而他没有的,那就是对终结点地址和绑定的建立,而且在使用服务引用的时候我们也没有提供之两样东西,直接掉服务协定方法就行了(见第一篇),那么服务引用是从哪里找到这两个关键的元数据元素呢?

就在配置文件里,我们做的客户端还没有配置文件,我们可以把这两个元素都放在配置文件里,这样就可以避免硬编码了。

为我们的控制台应用程序添加一个应用程序配置文件。方法是右键点击项目->添加->新建项->应用程序配置文件。保持默认名称app.config。

打开来看,里面没写什么实际的内容:

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

我们对配置服务应该不陌生,如果忘记了,翻回到第二篇去回顾一下。

首先还是要添加<System.ServiceModel>节,无论是服务端还是客户端,只要是WCF的服务配置都要在这个节里面:

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

在这里我们要配置的是客户端,所以我们不添加<Services>节了,而改成<Client>:

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

如果你忘记了我们在第三篇中配置的IIS服务的内容,可能稍有迷惑,这里的地址看上去是个服务地址而不是终结点地址,这是因为我们在IIS中把终结点地址设置为了空字符串,此时服务地址就是终结点地址了。注意看后面的contract,他的完全限定名的命名空间是客户端程序的命名空间ConsoleClient,这也表示这个类型是定义在本地的,不要搞错了。

保存,配置已经写完了,你如果看服务引用为我们生成的配置会看到一堆东西,实际上核心的就是这些。

既然我们已经在配置中声明了绑定和终结点地址,在代码中就不再需要了。

首先我们修改一下代理类,为他提供一个没有参数的构造函数,否则在new他的时候他会非管我们要两个参数。

  1. public HelloWCFClient()
  2. : base()
  3. {
  4.  
  5. }

还是什么也不做,直接调基类的无参构造函数。

然后修改一下Main函数,去掉终结点对象和地址对象的声明,并用无参构造函数来new 代理类的实例:

  1. static void Main(string[] args)
  2. {
  3. HelloWCFClient client = new HelloWCFClient();
  4.  
  5. string result = client.HelloWCF();
  6.  
  7. client.Close();
  8.  
  9. Console.WriteLine(result);
  10. Console.ReadLine();
  11. }

F5运行一下,结果如一吧!

到这里已经和服务引用的感觉基本一样了,我们已经写出了服务引用的核心部分。

以下是修改后的Program.cs完整代码:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5.  
  6. using System.ServiceModel;
  7.  
  8. namespace ConsoleClient
  9. {
  10. class Program
  11. {
  12. static void Main(string[] args)
  13. {
  14. HelloWCFClient client = new HelloWCFClient();
  15.  
  16. string result = client.HelloWCF();
  17.  
  18. client.Close();
  19.  
  20. Console.WriteLine(result);
  21. Console.ReadLine();
  22. }
  23. }
  24.  
  25. [ServiceContract]
  26. public interface IHelloWCF
  27. {
  28. [OperationContract]
  29. string HelloWCF();
  30. }
  31.  
  32. public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
  33. {
  34. public HelloWCFClient()
  35. : base()
  36. {
  37.  
  38. }
  39.  
  40. public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
  41. : base(binding, remoteAddress)
  42. {
  43.  
  44. }
  45.  
  46. public string HelloWCF()
  47. {
  48. return base.Channel.HelloWCF();
  49. }
  50. }
  51. }

7. 总结

通过本篇的学习,我们熟悉了另一种与服务端通信的办法,即通过ClientBase<>派生代理类的方法,这种方法实际上就是服务引用使用的方法,可以说我们已经在最简单级别上手写了一个服务引用的实现。

使用ChannelFactory<>和ClientBase<>都可以实现与服务端的通信,这是类库支持的最高层次的通讯方法了,其实还有更底层的通道通信方法,我们现在就不再深入了。而选择这两种方法完全取决于开发人员,有人喜欢工厂,有人喜欢代理类。既然我们都已经掌握了,就随意选择吧。

至此,我们对通信有了一个基本的了解,只能算一个初探吧。然而我相信这一次的小小深入会对我们今后的学习带来大大的帮助的。

绛河 初识WCF5的更多相关文章

  1. 绛河 初识WCF4

    参考: http://blog.csdn.net/anlun/article/details/44860821 第四篇 初探通信--ChannelFactory 通过前几篇的学习,我们简单了解了WCF ...

  2. 第四十节,requests模拟浏览器请求模块初识

    requests模拟浏览器请求模块初识  requests模拟浏览器请求模块属于第三方模块 源码下载地址http://docs.python-requests.org/zh_CN/latest/use ...

  3. requests模拟浏览器请求模块初识

    requests模拟浏览器请求模块初识  一.下载 requests模拟浏览器请求模块属于第三方模块 源码下载地址http://docs.python-requests.org/zh_CN/lates ...

  4. Android动画效果之初识Property Animation(属性动画)

    前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...

  5. 初识Hadoop

    第一部分:              初识Hadoop 一.             谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...

  6. python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)

    一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...

  7. 初识IOS,Label控件的应用。

    初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...

  8. UI篇(初识君面)

    我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...

  9. Python导出Excel为Lua/Json/Xml实例教程(一):初识Python

    Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...

随机推荐

  1. macOS High Sierra 10.13正式版USB安装盘制作

    1.首先,准备一个 8GB 或更大容量的 U盘,并备份好里面的所有资料. 2.下载好 macOS High Sierra 正式版的安装程序:https://support.apple.com/zh-c ...

  2. (转)[小工具] Linux下列格式化工具 - column

    当你看到Linux下命令输出的列歪歪扭扭时,是不是看着很不爽?column 命令就可以方便地解决这个问题. 比如: 我们一般就只用到上面这一个用法. column的其他用法如下: 选项 含义 -c 字 ...

  3. java 并发编程——Thread 源码重新学习

    Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...

  4. Hibernate中Query.list()方法报IllegalArgumentException异常

    最近在使用Hibernate开发项目,在写好hql语句,并初始化Query对象,执行Query.list()方法时,应用报IllegalArgumentException异常.经网上查询,现已经基本决 ...

  5. 在eclipse中启动Tomcat报端口被占用的错误

    安装配置好Tomcat之后,在浏览器中输入localhost,能正取打开页面.然后在eclipse中建立项目,创建Servlet之后,启动Tomcat,报端口被占用的错误.如图: 原因:原来已经启动了 ...

  6. 多线程编程(二)-Exchanger的使用

    Exchanger的介绍 类Exchanger的功能可以使两个线程之间传输数据. 方法exchange()的使用 package com.wjg.unit; import java.util.conc ...

  7. 10w定时任务,如何高效触发超时

    一.缘起 很多时候,业务有定时任务或者定时超时的需求,当任务量很大时,可能需要维护大量的timer,或者进行低效的扫描. 例如:58到家APP实时消息通道系统,对每个用户会维护一个APP到服务器的TC ...

  8. 穆里尼奥:曼联没有在今夏尝试过签下C罗

    在曼联结束的本个夏季首场友谊赛中,球队5-2战胜了洛杉矶银河,在赛后穆里尼奥出席了赛后的新闻发布会,并且回答了记者的提问.其中他表示曼联在今年夏季从来没有尝试回签C罗,因为这是“不可能完成的任务”. ...

  9. webstorm软件小技巧

    1.使用tab可以方便的生成代码片段 调出setting,搜索live template 在javascrpt 模板线面点击"+" 添加一个模板 fun 模板内容如下 functi ...

  10. PL/SQL之异常

    异常分为预定义异常和用户自定义异常.预定义异常是由系统定义的异常.由于他们已在STANDARD包中预定义了,因此,这些预定义异常可以直接在程序中使用,而不用在预定义部分声明.而用户自定义异常则需要在定 ...