WCF热带鱼书学习手记 - client coding
客户调用WCF service基本上有2种方式,当然啦,都是需要编程地……
1. 通过Proxy
一般来说我们通过SvcUtil.exe来获取WCF服务的元数据,进而生成客户端可直接调用的代理类。一般语法如下:
svcutil.exe http://localhost:9000/MyService/MyService.svc /out Proxy.cs
svcutil.exe http://localhost:9001/MEX /out Proxy.cs
对于服务端如下的代码,客户端代理类会生成相应的调用类。
服务端:
[ServiceContract(Namespace="MyNamespace")] interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { .... } }
客户端代理类:
[ServiceContract(Namespace="MyNamespace")] interface IMyContract { [OperationContract(Action="MyNamespace/IMyContract/MyMethod", ReplyAction="MyNamespace/IMyContract/MyMethodResponse")] void MyMethod(); } public partial class MyContractClient : ClientBase<IMyContract>, IMyContract { public MyContractClient() {} public MyContractClient(string endpointName) : base(endpointName) {} public MyContractClient(Binding binding, EndpointAddress remoteAddress) : base(binding, remoteAddress) {} public void MyMethod() { Channel.MyMethod(); } }
虽然是自动生成的代码,我们稍微看一下也是很有必要的。Proxy同样会生成IMyContract接口提供给客户端掉用。在理论上,我们可以通过这个和服务端定义相同的接口做到透明的调用服务公开的方法。这样做的基础在于,WCF客户端代理类的实际操作类实现了IMyContract类,同时在实现类封装了WCF客户端-服务端通信的所有细节。程序员不需要再显示的创建那些创建连接,设置连接,打开连接,调用方法这种东西啦。就跟写服务端代码一样,直接以IMyContract引用调用服务端方法。这个也就是同志们经常听到的“基于接口编程”的好处。
在调用代理类之前呢,还需要配置客户端WCF信息,这些东西因为是可能发生变化的部分,所以不可能固化在代理类里面,所以写在配置文件里面比较合适,反正也比较简单,基本上直接从服务端直接copy过来,然后修改一下就OK!
<system.serviceModel> <client name="MyNamespace.MyService"> <endpoint contract = "MyNamespace.IMyContract" binding = "wsHttpBinding" address = "http://localhost:8000/MyService" /> </client > </system.serviceModel>
然后就可以创建代理类实例了:
MyContractClient proxy = new MyContractClient(); //如果配置了多个endpoint,需要指定endpoint名字 proxy.MyMethod(); proxy.Close();
不过我更喜欢下面的写法,看起来就和写服务端代码一样了:
IMyContract myService = new MyContractClient(); myService .MyMethod(); myService .Close();
我们看MyContractClient(Binding binding, EndpointAddress remoteAddress)的构造函数,可以知道,除了可以通过配置文件设置endpoint信息,也能以变成的方式来搞定。不过相对前一种方法,这更像是hard coding,不推荐使用。知道有这种方法就行了。
2. 通过Channel
代理类有个不好的地方,当服务端契约发生变化的时候需要重新生成代理,看起来不是什么大问题,不过我在开发过程中确实遇到过类似的异常指出服务端与客户端类定义不匹配。。。不需要生成代理类的话,使用ChannelFactory是非常灵活的。这里不多说细节,给出代码先,使用起来是比较简单的。
同样的,在配置client endpoint好以后:
ChannelFactory<IMyContract> factory = new ChannelFactory<IMyContract>(); IMyContract proxy = factory.CreateChannel(); using(proxy as IDisposable) { proxy.MyMethod(); }
如果没有配置endpoint的话,编程方式搞定就行:
Binding binding = new NetTcpBinding(); EndpointAddress address = new EndpointAddress("net.tcp://localhost:9000"); IMyContract proxy = ChannelFactory<IMyContract>.CreateChannel(binding, address); using(proxy as IDisposable) { proxy.MyMethod(); }
其实基本上一样吧,因为看上面用proxy的例子,最终调用的也是一个channel...