绛河 初识WCF5
然后我们在<Client>中添加一个终结点,这个是客户端的终结点,我们前面曾经提过,通信实际上发生在两个终结点间,客户端也有个终结点,然而请求总是从客户端首先发起,所以终结点地址应该填写为服务端终结点的地址让客户端来寻址,但是服务协定要客户端本地是有的,所以这个要写本地定义的那个协定的完全限定名:
引用http://blog.csdn.net/songyefei/article/details/7389186
第五篇 再探通信--ClientBase
在上一篇中,我们抛开了服务引用和元数据交换,在客户端中手动添加了元数据代码,并利用通道工厂ChannelFactory<>类创建了通道,实现了和服务端的通信。然而,与服务端通信的编程模型不只一种,今天我们来学习利用另外一个服务类ClientBase<>来完成同样的工作,了解了这个类的使用方法,我们对服务引用中的关键部分就能够理解了。
ClientBase<>类也是一个泛型类,接受服务协定作为泛型参数,与ChannelFactory<>不同的是,这个类是一个基类,即抽象类,是不能实例化成对象直接使用的,我们需要自己写一个类来继承这个类,我们新写的类实例化出来就是客户端代理了,这个对象可以调用基类的一些受保护的方法来实现通信。ClientBase<>为我们封装的很好,我们只需要写个新类来继承他就可以了,通信的很多部分他都替我们做好了,比如我们不用进行去创建通道和打开通道的操作,直接调用协定方法就可以了。这也是服务引用和其他元数据生成工具(如svcutil)使用这个类来构造客户端代理的原因。
我们一边动手一边学习
1. 建立客户端程序
我们依然不用服务引用,完全手写,这次还是建立一个控制台应用程序作为客户端。
2. 添加必要的引用
我们用到的ClientBase<>类在System.ServiceModel程序集中,因此,要添加这个程序集的引用,同时在program.cs中添加using语句
- using System.ServiceModel;
这一步应该很熟了,如果有疑问,返回前几篇温习一下。
因为没有使用服务引用,客户端现在没有任何元数据的信息,我们要来手写。首先把服务协定写进去。这个再熟悉不过了(写在Program类后面):
[ServiceContract]
public interface IHelloWCF
{
[OperationContract]
string HelloWCF();
}
4. 编写客户端代理类
上面已经提到,我们要自己写一个新类来继承ClientBase<>基类,这样这个新类就是代理类了,同时,为了能够用代理类直接调用服务协定的方法,我们还要让代理类实现服务协定的接口,注意,继承要写在前面,实现接口要写在后面。我们把这个类起名为HelloWCFClient。
public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
{ }
ClientBase<>有许多的构造函数,接受不同种类的参数来创建代理类对象,其实这些参数都是元数据信息,刚才我们已经通过泛型参数传递给基类服务协定这个元数据了,现在基类还需要绑定和终结点地址这两个元数据才能正确创建连接,所以我们继承的新类应该把这个构造函数给覆载一下接受这两种元数据参数并传递给基类。
public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
{
public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{ }
}
我们看到这个新建的构造函数什么也没做,只是接受了两个参数,一个是绑定,一个是终结点地址,然后直接调用基类(也就是ClientBase<>)的构造函数,把这个两个参数传递了上去。其实工作都是ClientBase<>做的,我们新建的类就是个传话的,要不然怎么叫代理呢,他什么活都不干。
既然我们实现了服务协定接口,当然要实现接口的方法了。下面我们把方法的实现写下来:
public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
{
public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{ } public string HelloWCF()
{
return base.Channel.HelloWCF();
}
}
别忘了你的处境,凡人!我们这是在客户端啊,怎么可能有服务协定呢?这个可以有,但是这个实现不是我们在做,而是要和服务端通信让服务端做,这里可以看到代理鲜明的特点了,代理类虽然实现了服务协定的方法,但是在方法中,他调用了基类(就是ClientBase<>)上的通道,并通过通道调用了了协定方法。此时,ClientBase<>已经为我们建立好与服务端的通道了,而且是用服务协定建立的,我们当然可以在通道上调用服务协定的方法。所以调用代理类对象的HelloWCF()的过程是代理类委托基类在已经建立好的服务协定通道上调用协定方法,并从服务端获得返回值,然后再返回给代理类对象的调用者。狗腿啊狗腿。
5. 编写程序主体
代理类已经写完了,我们现在开始写程序的主体,让我们来到Program的Main函数中。
还是有一些准备要做,还差两个元数据呢,对了,就是绑定和地址。和上一篇一样,我们先建立这个两个元数据的对象备用:
WSHttpBinding binding = new WSHttpBinding();
EndpointAddress remoteAddress = new EndpointAddress("http://localhost/IISService/HelloWCFService.svc");
接下来就要建立我们的代理类对象了,new一个出来吧:
HelloWCFClient client = new HelloWCFClient(binding, remoteAddress);
把我们刚刚建立的两个元数据对象作为参数传递进去。
接下来就可以调用服务协定方法了:
string result = client.HelloWCF();
别忘了关闭通道,ClientBase<>很贴心的为我们准备了这个方法,不用再做强制类型转换什么的了(见前一篇)。
client.Close();
以下是Program.cs的全部代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.ServiceModel; namespace ConsoleClient
{
class Program
{
static void Main(string[] args)
{
WSHttpBinding binding = new WSHttpBinding();
EndpointAddress remoteAddress = new EndpointAddress("http://localhost/IISService/HelloWCFService.svc"); HelloWCFClient client = new HelloWCFClient(binding, remoteAddress); string result = client.HelloWCF(); client.Close(); Console.WriteLine(result);
Console.ReadLine();
}
} [ServiceContract]
public interface IHelloWCF
{
[OperationContract]
string HelloWCF();
} public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
{
public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{ } public string HelloWCF()
{
return base.Channel.HelloWCF();
}
}
}
6. 再展开一点点
这样看上去已经挺成功了,我们现在再打开服务引用的reference.cs代码,看看是不是大部分都看得懂了?监管有些地方他写的可能有些复杂,比如描述协定的属性参数啊,代理类更多的构造函数啊,但是核心的就是我们刚刚写的部分,那一大堆wsdl什么的其实都不是核心,不信的话你把他们都删掉,就留一个reference.cs看看还好不好用?那一堆东西也不是没用就是现在自己看起来还蛮复杂的,我们到后面一点点学习。
我们仔细看服务引用的reference.cs代码,有一样东西是我们有而他没有的,那就是对终结点地址和绑定的建立,而且在使用服务引用的时候我们也没有提供之两样东西,直接掉服务协定方法就行了(见第一篇),那么服务引用是从哪里找到这两个关键的元数据元素呢?
就在配置文件里,我们做的客户端还没有配置文件,我们可以把这两个元素都放在配置文件里,这样就可以避免硬编码了。
为我们的控制台应用程序添加一个应用程序配置文件。方法是右键点击项目->添加->新建项->应用程序配置文件。保持默认名称app.config。
打开来看,里面没写什么实际的内容:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>
我们对配置服务应该不陌生,如果忘记了,翻回到第二篇去回顾一下。
首先还是要添加<System.ServiceModel>节,无论是服务端还是客户端,只要是WCF的服务配置都要在这个节里面:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel> </system.serviceModel>
</configuration>
在这里我们要配置的是客户端,所以我们不添加<Services>节了,而改成<Client>:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client> </client>
</system.serviceModel>
</configuration>
如果你忘记了我们在第三篇中配置的IIS服务的内容,可能稍有迷惑,这里的地址看上去是个服务地址而不是终结点地址,这是因为我们在IIS中把终结点地址设置为了空字符串,此时服务地址就是终结点地址了。注意看后面的contract,他的完全限定名的命名空间是客户端程序的命名空间ConsoleClient,这也表示这个类型是定义在本地的,不要搞错了。
保存,配置已经写完了,你如果看服务引用为我们生成的配置会看到一堆东西,实际上核心的就是这些。
既然我们已经在配置中声明了绑定和终结点地址,在代码中就不再需要了。
首先我们修改一下代理类,为他提供一个没有参数的构造函数,否则在new他的时候他会非管我们要两个参数。
public HelloWCFClient()
: base()
{ }
还是什么也不做,直接调基类的无参构造函数。
然后修改一下Main函数,去掉终结点对象和地址对象的声明,并用无参构造函数来new 代理类的实例:
static void Main(string[] args)
{
HelloWCFClient client = new HelloWCFClient(); string result = client.HelloWCF(); client.Close(); Console.WriteLine(result);
Console.ReadLine();
}
F5运行一下,结果如一吧!
到这里已经和服务引用的感觉基本一样了,我们已经写出了服务引用的核心部分。
以下是修改后的Program.cs完整代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.ServiceModel; namespace ConsoleClient
{
class Program
{
static void Main(string[] args)
{
HelloWCFClient client = new HelloWCFClient(); string result = client.HelloWCF(); client.Close(); Console.WriteLine(result);
Console.ReadLine();
}
} [ServiceContract]
public interface IHelloWCF
{
[OperationContract]
string HelloWCF();
} public class HelloWCFClient : ClientBase<IHelloWCF>, IHelloWCF
{
public HelloWCFClient()
: base()
{ } public HelloWCFClient(System.ServiceModel.Channels.Binding binding, EndpointAddress remoteAddress)
: base(binding, remoteAddress)
{ } public string HelloWCF()
{
return base.Channel.HelloWCF();
}
}
}
7. 总结
通过本篇的学习,我们熟悉了另一种与服务端通信的办法,即通过ClientBase<>派生代理类的方法,这种方法实际上就是服务引用使用的方法,可以说我们已经在最简单级别上手写了一个服务引用的实现。
使用ChannelFactory<>和ClientBase<>都可以实现与服务端的通信,这是类库支持的最高层次的通讯方法了,其实还有更底层的通道通信方法,我们现在就不再深入了。而选择这两种方法完全取决于开发人员,有人喜欢工厂,有人喜欢代理类。既然我们都已经掌握了,就随意选择吧。
至此,我们对通信有了一个基本的了解,只能算一个初探吧。然而我相信这一次的小小深入会对我们今后的学习带来大大的帮助的。
绛河 初识WCF5的更多相关文章
- 绛河 初识WCF4
参考: http://blog.csdn.net/anlun/article/details/44860821 第四篇 初探通信--ChannelFactory 通过前几篇的学习,我们简单了解了WCF ...
- 第四十节,requests模拟浏览器请求模块初识
requests模拟浏览器请求模块初识 requests模拟浏览器请求模块属于第三方模块 源码下载地址http://docs.python-requests.org/zh_CN/latest/use ...
- requests模拟浏览器请求模块初识
requests模拟浏览器请求模块初识 一.下载 requests模拟浏览器请求模块属于第三方模块 源码下载地址http://docs.python-requests.org/zh_CN/lates ...
- Android动画效果之初识Property Animation(属性动画)
前言: 前面两篇介绍了Android的Tween Animation(补间动画) Android动画效果之Tween Animation(补间动画).Frame Animation(逐帧动画)Andr ...
- 初识Hadoop
第一部分: 初识Hadoop 一. 谁说大象不能跳舞 业务数据越来越多,用关系型数据库来存储和处理数据越来越感觉吃力,一个查询或者一个导出,要执行很长 ...
- python学习笔记(基础四:模块初识、pyc和PyCodeObject是什么)
一.模块初识(一) 模块,也叫库.库有标准库第三方库. 注意事项:文件名不能和导入的模块名相同 1. sys模块 import sys print(sys.path) #打印环境变量 print(sy ...
- 初识IOS,Label控件的应用。
初识IOS,Label控件的应用. // // ViewController.m // Gua.test // // Created by 郭美男 on 16/5/31. // Copyright © ...
- UI篇(初识君面)
我们的APP要想吸引用户,就要把UI(脸蛋)搞漂亮一点.毕竟好的外貌是增进人际关系的第一步,我们程序员看到一个APP时,第一眼就是看这个软件的功能,不去关心界面是否漂亮,看到好的程序会说"我 ...
- Python导出Excel为Lua/Json/Xml实例教程(一):初识Python
Python导出Excel为Lua/Json/Xml实例教程(一):初识Python 相关链接: Python导出Excel为Lua/Json/Xml实例教程(一):初识Python Python导出 ...
随机推荐
- android中画弧函数canvas.drawArc()之理解
在学习android中图形图像处理技术这部分内容时,对绘制圆弧函数canvas.drawArc()的用法.参数含义及画图原理很是不理解,在网上搜索了一些,加上自己的理解,在此做个小总结,作为学习过程中 ...
- ubuntu init启动流程
ubuntu的init方式有两种:一种是System V initialization,一种是Upstart.ubuntu6.10以前的版本是第一种方式,之后的版本是第二种方式. 在旧式的System ...
- 多线程并发问题解决之redis锁
一 问题背景 我们做的是医疗信息化系统,在系统中一条患者信息对医院中当前科室中的所有诊断医生是可见的,当有一个诊断医生点击按钮处理该数据时,数据的状态发生了变化,其他的医生就不可以再处理此患者的数据了 ...
- bootstrap中对dropdown使用hover代替click
bootstrap的下拉组件需要点击click才能展示下拉列表,这在使用导航的时候很不方便因此有一个扩展的组件来解决这个问题. 在VS的Nuget中查询bootstrap-hover-dropdown ...
- 使用Jprofiler+jmeter进行JVM性能调优
一.JProfiler简介 JProfiler 是一个商业授权的Java剖析工具,由EJ技术有限公司,针对的Java EE和Java SE应用程序开发的.它把CPU.执行绪和内存的剖析组合在一个强大的 ...
- Linux笔记-vim 配置
本文是转载的,我用的ubuntu12.04在vim设置方面就是参考了本文,拿来分享给大家! ubuntu10.10中的设置方法: $cd /etc/vim $sudo cp vimrc vimrc.b ...
- [转]如何配置Log4Net使用Oracle数据库记录日志
本文转自:http://www.cnblogs.com/PatrickLiu/p/6012153.html 最近在做一个项目的时候,需要增加一个日志的功能,需要使用Log4Net记录日志,把数据插入到 ...
- [转]ASP.NET web API 2 OData enhancements
本文转自:https://www.pluralsight.com/blog/tutorials/asp-net-web-api-2-odata-enhancements Along with the ...
- Python——如何搭建Python的环境
最近在学Python,只知道python一般是用来写爬虫的,以前看过一个朋友用Python做的爬虫从妹子图网站上下载图片,觉得很有趣,自己也想学一学. 俗话说,万事开头难,首先第一步就是搭建Pytho ...
- NetSugar.Cap与CAP功能比对
前言 首先非常感谢开源社区,在各位作者无私得奉献下,我才有幸接触CAP.在拜读源码和理解设计原理过程中,发现CAP的源码是一个非常值得我们学习的代码.本人代码的基本框架采用简单的DDD,在练习Demo ...