1.3.5 步骤五 创建客户端调用服务
服务被成功寄宿后,服务端便开始了服务调用请求的监听工作。此外,服务寄宿将服务描述通过元数据的形式发布出来,相应的客户端就可以获取这些元数据,创建客户端程序进行服务的消费。在VS下,当我们添加服务引用的时候,VS在内部帮我们实现元数据的获取,并借助这些元数据通过代码生成工具(SvcUtil.exe)自动生成用于服务调用的服务代理相关代码和相应的配置。
在运行服务寄宿程序(Hosting.exe)的情况下,右键点击Client项目,在弹出的上下文菜单中选择“添加服务引用(Add Service References)”,如图1-7所示的添加服务引用的对话会显示出来。在地址栏上键入服务元数据发布的源地址:http://127.0.0.1:9999/calculatorservice/metadata,并指定一个命名空间,点击OK按钮,VS为你生成一系列用于服务调用的代码和配置。
图1-7 添加服务引用
在一系列自动生成的类中,包含一个服务契约接口、一个服务代理对象和其他相关的类。被客户端直接用于服务调用的是一个继承自ClientBase<CalculatorService>并实现了CalculatorService接口(CalculatorService为客户端生成的服务契约接口类型)的服务代理类。ClientBase<CalculatorService>的定义如下所示:
namespace Artech.WcfServices.Client.CalculatorServices { //其他类型成员 [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] public partial class CalculatorServiceClient : System.ServiceModel. ClientBase<Artech.WcfServices.Client.CalculatorServices. CalculatorService>, Artech.WcfServices.Client.CalculatorServices. CalculatorService { public CalculatorServiceClient() { } public CalculatorServiceClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public CalculatorServiceClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public double Add(double x, double y) { return base.Channel.Add(x, y); } public double Subtract(double x, double y) { return base.Channel.Subtract(x, y); } public double Multiply(double x, double y) { return base.Channel.Multiply(x, y); } public double Divide(double x, double y) { return base.Channel.Divide(x, y); } }
我们可以创建CalculatorServiceClient对象,执行相应方法调用服务操作。客户端进行服务调用的代码如下:
using System; using Artech.WcfServices.Client.CalculatorServices; namespace Artech.WcfServices.Client { class Program { static void Main(string[] args) { using (CalculatorServiceClient proxy = new CalculatorServiceClient()) { Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2)); Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2)); Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2)); Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2)); } } } }
运行后输出:
x + y = 3 when x = 1 and y = 2 x - y = -1 when x = 1 and y = 2 x * y = 2 when x = 1 and y = 2 x / y = 0.5 when x = 1 and y = 2
客户端通过服务代理对象进行服务的调用,上面的例子通过创建自动生成的、继承自ClientBase<T>的类型对象进行服务调用。实际上,我们还有另外一种创建服务代理的方法,就是通过ChannelFactory<T>。此外,WCF采用基于契约的服务调用方法,从上面的例子我们也可以看到,VS在进行服务引用添加的过程中,会在客户端创建一个与服务端等效的服务契约接口。在上面的例子中,由于服务端和客户端都在同一个解决方案中,完全可以让服务端和客户端引用相同的契约。
为了演示这种场景,我们将添加的服务引用移除,并为Client项目添加对Contracts项目的引用。借助于这个服务契约,并通过ChannelFactory<ICalculator>创建服务代理对象,直接进行相应的服务调用。下面的代码演示了基于ChannelFacotory<T>进行服务代理的创建和服务调用的方式。
using System; using System.ServiceModel; using Artech.WcfServices.Contracts; namespace Artech.WcfServices.Client { class Program { static void Main(string[] args) { using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(new WSHttpBinding(), "http://127.0.0.1:9999/calculatorservice")) { ICalculator proxy = channelFactory.CreateChannel(); using (proxy as IDisposable) { Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 1, 2, proxy.Add(1, 2)); Console.WriteLine("x - y = {2} when x = {0} and y = {1}", 1, 2, proxy.Subtract(1, 2)); Console.WriteLine("x * y = {2} when x = {0} and y = {1}", 1, 2, proxy.Multiply(1, 2)); Console.WriteLine("x / y = {2} when x = {0} and y = {1}", 1, 2, proxy.Divide(1, 2)); } } } } }
由于终结点是WCF进行通信的唯一手段,ChannelFactory<T>本质上是通过指定的终结点创建用于进行服务调用的服务代理。在上面的代码中,在创建ChannelFactory<T>的时候再在构造函数中指定终结点的相关要素(契约通过泛型类型表示,地址和绑定则通过参数指定)。在真正的WCF应用中,大都采用配置的方式进行终结点的定义。我们可以通过下面的配置指定终结点的三要素,并为相应的终结点指定一个终结点配置名称(calculatorservice)。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <client> <endpoint address="http://127.0.0.1:9999/calculatorservice" binding="wsHttpBinding" contract="Artech.WcfServices.Contracts. ICalculator" name="calculatorservice" /> </client> </system.serviceModel> </configuration>
那么在创建ChannelFactory<T>的时候,就无须再指定终结点的绑定和地址了,只须制定对应的终结点配置名称。
using System; using System.ServiceModel; using Artech.WcfServices.Contracts; namespace Artech.WcfServices.Client { class Program { static void Main(string[] args) { using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>( "calculatorservice")) { //省略代码 } } } }