WCF技术剖析(卷1)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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"))
                    {
                    //省略代码
                    }
                }
            }
        }