WCF Essentials (1)

来源:互联网 发布:中国联通wap网络 编辑:程序博客网 时间:2024/06/05 04:25

本文是对 Juval Lowy《Programming WCFServices》第一章的简要翻译。当然,为了照顾中文书写和阅读习惯,翻译文字和原文并非一一对应的。也许是《MSDN中文版》或者国内其他一些翻译书籍给我带来的坏印象,我非常小心重新组织文字,避免出现那些让人看不懂的机器译文。本文只是个人学习笔记,雨痕完全尊重原文作者的全部权益,不持有本文的任何版权,转载时请注明原文作者。

1. 什么是 WCF?

WindowsCommunication Foundation (WCF) 是一套 Windows平台上开发和发布服务(services)的软件开发工具包(SDK)。WCF 提供服务运行环境,使你能将 CLR类型暴露为服务,或者反过来将服务封装为 CLR 类型。尽管理论上 WCF 不是必须的,但在实际开发中 WCF会让我们的工作变得更加简单。WCF 是微软实现的一套工业标准,它定义了服务交互(service interactions)、类型转换(typeconversion) 、封装(marshaling)、多协议管理(various protocols'management)等技术细节。也正因为其标准化,WCF 可以支持不同平台服务之间的互操作。WCF为开发者提供了各种不同应用需求的支持,可大幅提高生产力。在 WCF 的第一个版本(firstrelease)中包含了许多实用功能,比如宿主(hosting)、服务实例管理(service instancemanagement)、异步调用(asynchronous calls)、可靠性(reliability)、事务管理(transactionmanagement)、离线队列调用(disconnected queued calls)、安全(security)等等。WCF拥有一套优雅(elegant)、可扩展(extensibility)的模型,WCF 本身就是基于该模型开发的。

2. 服务 (Services)

一个服务就是一系列功能的组合。软件开发一直在进化着,从早期的结构化函数(functions)到对象(objects)、组件(components),直到现在的服务方式。Service-orientation (SO) 是一套构建 "面向服务程序"的抽象原则和最优方法。SOA 程序 (service-oriented application)将服务集中到单个逻辑程序当中,就像组件服务程序或面向对象程序那样。

uploads/200710/20_140335_1.gif


SOA 示意图

服务可以是本地或远程的,其客户端可以是 Windows Froms、ASP.NETpage,甚至是其他服务。服务和客户端之间通过发送和接收消息(messages)来相互影响,消息可以直接传递,也可以通过代理服务器等方式进行中介传送。WCF 中的消息都是 SOAP 格式,但这和 Web services 无关。WCF 服务可使用多种传输方式,并不仅仅是 HTTP协议。WCF 服务可以被非 WCF 客户端(non-WCF clients)调用,同样 WCF 客户端也可以调用非 WCF服务(non-WCF services)。

由于服务构成对外界是非透明的,因此 WCF通过元数据(metadata)来描述其可用功能和通讯方式。元数据事先通过技术中立的方式被发布,比如WSDL/HTTP-GET,以及其他标准的元数据交换模式(metadata exchange)。非 WCF客户端可以导入元数据,生成其自身平台类型的调用代码。同样,WCF 客户端也能完成同样的功能。

服务执行边界

客户端从来不会直接调用服务,即便这个服务就在本机的内存中。客户端总是通过一个代理(proxy)来转发调用,代理拥有和服务相同的操作接口,当然还有一些附加的代理管理方法。

WCF 允许客户端跨越所有的边界与服务进行通讯。通过下面这些图,我们可以了解其通讯方式。

uploads/200710/20_140513_2.gif


相同主机通讯模式

uploads/200710/20_140518_3.gif


跨机器通讯模式

3. 地址 (Addresses)

每个服务都会关联到一个唯一的地址。地址提供了两个重要信息: 服务位置(location)、传送协议(transportprotocol)。WCF 1.0 支持 HTTP、TCP、Peer network、IPC (Inter-ProcessCommunication over named pipes) 和 MSMQ。

下面是一些常用地址的例子。

http://localhost:8001
http://localhost:8001/MyService
net.tcp://localhost:8002/MyService
net.pipe://localhost/MyPipe
net.msmq://localhost/private/MyService
net.msmq://localhost/MyService


4. 契约 (Contracts)

契约是用来描述服务功能(service does)的一种平台中立的标准方式,WCF 所有服务都需要实现一个或多个契约。

WCF 定义了四种类型的契约:

  • Service contracts : 定义客户端可以使用哪些服务操作。
  • Data contracts : 定义服务传输的数据类型。WCF 定义了一些隐式数据契约,像 int、string 等,更多时候我们需要使用 DataContractAttribute 显式定义那些自定义数据结构的数据契约。
  • Fault contracts : 定义服务引发的错误信息,以及如何传递这些异常给客户端。
  • Message contracts : 允许我们直接操控服务消息内容和格式,可以是类型化或无类型的。

在 WCF 中,我们通过 ServiceContractAttribute 和 OperationContractAttribute 特性来定义服务契约。

[AttributeUsage(AttributeTargets.Interface|AttributeTargets.Class, Inherited = false)]
public sealed class ServiceContractAttribute : Attribute
{
  public string Name {get;set;}
  public string Namespace {get;set;}
  //More members
}

[AttributeUsage(AttributeTargets.Method)]
public sealed class OperationContractAttribute : Attribute
{
  public string Name {get;set;}
  //More members
}


ServiceContractAttribute将接口或类型映射为一个技术中立(technology-neutral)的服务契约,ServiceContractAttribute标注的服务契约不受 CLR 类型访问限定规则(type's visibility)限制,也就是说我们将ServiceContractAttribute 合法地标注在一个 internal interface上。这是因为契约本身是一种规则描述,只不过在 WCF 中我们使用 ServiceContractAttribute来映射这种规则,客户端并不会直接依赖于这个具体的接口或类型。

另外,我们还需要显式使用OperationContractAttribute 来标注服务操作。OperationContractAttribute只能用于方法(methods),无论这个方法是 private 还是public。而那些没有标注该特性的方法并不会成为契约的一部分。契约操作方法的参数必须是基本类型或其他可序列化的引用类型(Serializable、DataContract)。

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  string MyMethod( );
}

class MyService : IMyContract
{
  public string MyMethod( )
  {
    return "Hello WCF";
  }
}


单个类型可以实现多个服务契约。

[ServiceContract]
interface IMyContract
{
  [OperationContract]
  string MyMethod( );
}

[ServiceContract]
interface IMyOtherContract
{
  [OperationContract]
  void MyOtherMethod( );
}

class MyService : IMyContract,IMyOtherContract
{
  public string MyMethod( ) {...}
  public void MyOtherMethod( ) {...}
}


通常情况下,WCF 只能调用使用默认构造方法。虽然我们可以绕开这个限制,但这个服务实例模式只能是 Single。

[ServiceContract]
interface IMyService
{
  [OperationContract]
  void Test(int x);
}

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
class MyService : IMyService
{
  private int x;

  public MyService(int x)
  {
    this.x = x;
  }

  public void Test(int x)
  {
    this.x += x;
    Console.WriteLine(this.x);
  }
}

class Program
{
  static void Main(string[] args)
  {
    var o = new MyService(1234);
    var host = new ServiceHost(o, new Uri("http://localhost:802"));
    host.AddServiceEndpoint(typeof(IMyService), new BasicHttpBinding(), "");
    host.Open();

    // ---- Client ------------
    
    var channel = ChannelFactory<IMyService>.CreateChannel(new BasicHttpBinding(),
      new EndpointAddress("http://localhost:802"));
      
    using (channel as IDisposable)
    {
      channel.Test(1);
    }

    Console.WriteLine("Press any key to exit...");
    Console.ReadKey(true);
    Environment.Exit(0);
  }
}


除了将接口映射为服务契约外,我们还可以直接在 Class 上标注。

[ServiceContract]
class MyService
{
  [OperationContract]
  string MyMethod( )
  {
    return "Hello WCF";
  }
}


和 Web Services 开发一样,我们通过为契约添加名字空间(namespace)来避免命名冲突。未做设置时,默认名字空间是 http://tempuri.org。

[ServiceContract(Namespace = "MyNamespace")]
interface IMyContract {...}


缺省情况下,服务契约的名称直接使用类型和方法的名字。我们可以使用 Name 属性来定义一个不同的对外公开名称。

[ServiceContract(Name = "IMyOtherContract")]
interface IMyContract
{
  [OperationContract(Name = "SomeOperation")]
  void MyMethod(string text);
}