WCF Essentials (6)

来源:互联网 发布:想开淘宝店怎么注册 编辑:程序博客网 时间:2024/06/12 18:11

9. 客户端编程

在调用服务操作之前,客户端必须导入服务契约。如果客户端同样使用 WCF环境,那么通常的做法是使用代理。代理是一个拥有与服务契约相同接口样式的 CLR类(class)。如果服务实现了多个契约,那么客户端需要为每个契约类型生成一个代理。代理除了提供和服务契约相同的操作方法外,还拥有一些附加方法完成诸如代理生命周期管理、服务连接等操作。代理完整囊括了服务的各个方面:服务定位、实现技术、运行平台以及通讯传输。

生成代理

我们可以使用 Visual Studio 2008导入服务元数据来生成代理。如果服务是自宿主(self-hosted),那么需要我们在导入操作前启动服务进程。在解决方案管理器(SolutionExplorer)中项目服务引用(Project Service References)添加引用即可。如果服务宿主在 IIS 或 WAS中,我们无需事先启动服务,它们会在接收到请求时自动为我们准备好服务环境。

uploads/200711/18_204238_1.png



在添加服务引用的对话框中,我们需要输入服务的元数据地址(或者 MEX URI)、命名空间(namespace)。

uploads/200711/18_204242_2.png



我们还可以使用 SvcUtil.exe 命令行工具代替 Visual Studio。将服务的 HTTP-GET 或者 MEX地址、代理文件名作为参数提交给 SvcUtil 即可。缺省的代理文件名是 output.cs,我们可以使用 /out 参数指定一个不同的文件名。

举个例子,如果你的服务 MyService 宿主在 IIS 或者 WAS 中,你已经允许了元数据通过 HTTP-GET 进行发布,那么你可以用下面这样的命令行生成代理。

SvcUtil http://localhost/MyService/MyService.svc /out:Proxy.cs


如果默认端口不是 80,则必须在地址中指定正确的端口。

SvcUtil http://localhost:81/MyService/MyService.svc /out:Proxy.cs


下面是一些不同协议生成代理的例子。

SvcUtil http://localhost:8002/MEX  /out:Proxy.cs
SvcUtil http://localhost:8002/  /out:Proxy.cs
SvcUtil net.tcp://localhost:8003/MEX  /out:Proxy.cs
SvcUtil net.pipe://localhost/MyPipe/MEX  /out:Proxy.cs


SvcUtil 比 Visual Studio 拥有更强大的功能,它提供了更多的生成选项。

我们用下面这个服务做为演示,看看自动生成的代理类型。

[ServiceContract(Namespace = "MyNamespace")]
interface IMyContract
{
  [OperationContract]
  void MyMethod( );
}

class MyService : IMyContract
{
  public void MyMethod( ) {...}
}


以下就是自动生成的代码,为了便于阅读,做了些精简。

[ServiceContract(Namespace = "MyNamespace")]
public 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( );
  }
}


客户端配置

客户端必须知道服务的位置,并且要使用相同的服务绑定。本质上,我们可以通过目标服务端点来获得这些服务信息。通常用客户端配置文件中来包含这些服务端点的配置参数。

<system.serviceModel>
  <client>
    <endpoint name = "MyEndpoint"
      address = "http://localhost:8000/MyService/"
      binding = "wsHttpBinding"
      contract = "IMyContract"
    />
  </client>
</system.serviceModel>


配置文件中可以包含多个服务端点设置,客户端可以通过名称(name)属性从中选择一个进行连接。

<system.serviceModel>
  <client>
    <endpoint name = "FirstEndpoint"
      address = "http://localhost:8000/MyService/"
      binding = "wsHttpBinding"
      contract = "IMyContract"
    />
    <endpoint name = "SecondEndpoint"
      address = "net.tcp://localhost:8001/MyService/"
      binding = "netTcpBinding"
      contract = "IMyContract"
    />
    <endpoint name = "ThirdEndpoint"
      address = "net.tcp://localhost:8002/MyService/"
      binding = "netTcpBinding"
      contract = "IMyOtherContract"
    />
  </client>
</system.serviceModel>


除了端点,我们还可以对客户端绑定进行一些自定义设置。

<system.serviceModel>
  <client>
    <endpoint name = "MyEndpoint"
      address = "net.tcp://localhost:8000/MyService/"
      bindingConfiguration = "TransactionalTCP"
      binding = "netTcpBinding"
      contract = "IMyContract"
    />
  </client>
  <bindings>
    <netTcpBinding>
      <binding name = "TransactionalTCP"
        transactionFlow = "true"
      />
    </netTcpBinding>
  </bindings>
</system.serviceModel>


实际上,SvcUtil 可以自动生成客户端配置文件。

SvcUtil http://localhost:8002/MyService/ /out:Proxy.cs /config:App.Config


我们也可以用 noconfig 参数来阻止 SvcUtil 生成配置文件。

SvcUtil http://localhost:8002/MyService/ /out:Proxy.cs /noconfig


对于 In-proc (客户端和服务器放在一个程序集中) 宿主方式,客户端和服务配置会放到一个配置文件中,下面就是一个演示。

<system.serviceModel>
  <services>
    <service name = "MyService">
      <endpoint
        address = "net.pipe://localhost/MyPipe"
        binding = "netNamedPipeBinding"
        contract = "IMyContract"
      />
    </service>
  </services>
  <client>
    <endpoint name = "MyEndpoint"
      address = "net.pipe://localhost/MyPipe"
      binding = "netNamedPipeBinding"
      contract = "IMyContract"
    />
  </client>
</system.serviceModel>


SvcConfigEditor

WCF 提供了一个图形化的配置文件编辑工具 —— Microsoft Service Configuration Editor (SvcConfigEditor.exe),它可以非常方便地生成服务端和客户端的配置文件。

uploads/200711/18_204247_3.png



有关该工具的使用,可参考《[WCF 学习笔记] 11. 配置文件》。

使用客户端代理

代理类继承自 ClientBase<T>。

public abstract class ClientBase<T> : ICommunicationObject, IDisposable
{
  protected ClientBase(string endpointName);
  protected ClientBase(Binding binding,EndpointAddress remoteAddress);
  public void Open( );
  public void Close( );
  protected T Channel {get;}
  //Additional members
}


ClientBase<T>接受一个标示服务契约类型的泛型参数。Channel属性就是该参数类型。要使用代理,客户端首先需要创建代理实例,我们可以在构造方法中指定具体的端点配置名称或者创建一个端点实例。客户端在调用完代理方法后,必须关闭代理实例。

MyContractClient proxy = new MyContractClient("MyEndpoint");
proxy.MyMethod( );
proxy.Close( );


如果配置文件中该服务契约只有一个端点设置,可以省略构造参数。

MyContractClient proxy = new MyContractClient( );
proxy.MyMethod( );
proxy.Close( );


推荐使用 using 调用 ClientBase<T>.Dispose 来关闭代理。

演示一

using(MyContractClient proxy = new MyContractClient( ))
{
  proxy.MyMethod( );
}


演示二

IMyContract proxy = new MyContractClient( ));
proxy.MyMethod( );
IDisposable disposable = proxy as IDisposable;
if(disposable != null)
{
  disposable.Dispose( );
}


演示三

IMyContract proxy = new MyContractClient( );
using(proxy as IDisposable)
{
  proxy.MyMethod( );
}


调用超时

客户端的每次调用都必须在一个可配置的超时时间内完成。无论什么原因导致调用超出预设超时限制,客户端都会引发一个 TimeoutException异常。我们可以通过绑定对象的 SendTimeout 属性来设置精确的超时时间,其默认值是一分钟(one minute)。

public abstract class Binding : ...
{
  public TimeSpan SendTimeout {get;set;}
  //More members
}


演示

<client>
  <endpoint
    ...
    binding = "wsHttpBinding"
    bindingConfiguration = "LongTimeout"
    ...
  />
</client>
<bindings>
  <wsHttpBinding>
    <binding name = "LongTimeout" sendTimeout = "00:05:00"/>
  </wsHttpBinding>
</bindings>


编程设置客户端配置

客户端可以用配置代码来代替配置文件。这需要我们创建一个包含服务地址的端点对象,一个相匹配的绑定对象,并将它们传递给代理构造方法。

public class EndpointAddress
{
  public EndpointAddress(string uri);
  //More members
}


演示

Binding wsBinding = new WSHttpBinding( );
EndpointAddress endpointAddress = new EndpointAddress("http://localhost:8000/MyService/");

MyContractClient proxy = new MyContractClient(wsBinding, endpointAddress);

proxy.MyMethod( );
proxy.Close( );


这种方式在使用上和配置文件没什么区别,我们同样可以用代码来设置绑定对象的相关参数。

WSHttpBinding wsBinding = new WSHttpBinding( );
wsBinding.SendTimeout = TimeSpan.FromMinutes(5);
wsBinding.TransactionFlow = true;

EndpointAddress endpointAddress = new EndpointAddress("http://localhost:8000/MyService/");

MyContractClient proxy = new MyContractClient(wsBinding,endpointAddress);
proxy.MyMethod( );
proxy.Close( );
原创粉丝点击