Programming WCF Services翻译笔记(四)

转载 2007年10月16日 09:38:00

 

本书的第2章主要讲解了服务契约。内容:“本章首先会讨论如何通过操作重载与契约层级,为两种迥然不同的编程模型建立关联。然后,本章会介绍一些简单而又强大的设计和分离服务契约的技术与指导原则。在本章末尾,还演示了如何通过编程方式在运行时实现与契约元数据的交互。”

操作重载

C++与C#均支持操作的重载,但在WCF的编程模型中,却并不支持这种技术。坦白说,在WCF的编程模型,对于面向对象的支持都是比较弱的,包括后面要介绍的继承体系与多态,都存在许多问题。因此,在服务端我们不能定义这样的服务契约:
[ServiceContract]
interface ICalculator
{
   [OperationContract]
   int Add(int arg1,int arg2);

   [OperationContract]
   double Add(double arg1,double arg2);
}

虽然在编译时能够通过,然而一旦在装载宿主时,就会抛出InvalidOperationException异常。以ICalculator契约为例,WCF会认为是零个操作。

解决的办法是利用OperationContract特性的Name属性,例如:
[ServiceContract]
interface ICalculator
{
   [OperationContract(Name = "AddInt")]
   int Add(int arg1,int arg2);

   [OperationContract(Name = "AddDouble")]
   double Add(double arg1,double arg2);
}

不过采用这种方式,存在的问题是生成的代理会将Name属性指定的名称作为代理操作的方法名。这对于编程者而言,并非好的方式。所幸我们可以手动对生成的代理进行修改,将它修改为与服务契约一致的操作名。由于,此时通过Name指定了操作的别名,因此,避免了装载宿主抛出的异常。

契约的继承

即使父接口标记了[ServiceContract],子接口仍然需要标记[ServiceContract],因为ServiceContractAttribute是不可继承的。服务类对服务契约的实现,与传统的C#编程没有什么区别。例如:
[ServiceContract]
interface ISimpleCalculator
{
   [OperationContract]
   int Add(int arg1,int arg2);
}
[ServiceContract]
interface IScientificCalculator : ISimpleCalculator
{
   [OperationContract]
   int Multiply(int arg1,int arg2);
}
class MyCalculator : IScientificCalculator
{
   public int Add(int arg1,int arg2)
   {
      return arg1 + arg2;
   }
   public int Multiply(int arg1,int arg2)
   {
      return arg1 * arg2;
   }
}

公开终结点的时候,可以对最底层的契约接口公开一个单独的终结点:
<service name=”MyCalculator”>
   <endpoint>
       <address=”http://localhost:8001/MyCalculator/”>
       <binding=”basicHttpBinding”>
       <contract=” IScientificCalculator”>
   </endpoint>
</service>

客户端在导入如上的服务契约时,会取消服务契约的继承层级,并利用OperationContract特性中的Action与ReplyAction属性,保留原来定义每个操作的契约名。但为了使客户端编程能够与服务编程保持一致,最好是恢复客户端的契约层级。方法并无什么太玄妙的地方,无非就是根据服务契约层级对客户端契约进行手工修改。修改后的客户端契约及其代理的定义如下:
[ServiceContract]
public interface ISimpleCalculator
{
   [OperationContract]
   int Add(int arg1,int arg2);
}
public partial class SimpleCalculatorClient : ClientBase<ISimpleCalculator>,
                                              ISimpleCalculator
{
   public int Add(int arg1,int arg2)
   {
      return Channel.Add(arg1,arg2);
   }
   //Rest of the proxy
}

[ServiceContract]
public interface IScientificCalculator : ISimpleCalculator
{
   [OperationContract]
   int Multiply(int arg1,int arg2);
}
public partial class ScientificCalculatorClient :
                           ClientBase<IScientificCalculator>,IScientificCalculator
{
   public int Add(int arg1,int arg2)
   {
      return Channel.Add(arg1,arg2);
   }
   public int Multiply(int arg1,int arg2)
   {
      return Channel.Multiply(arg1,arg2);
   }
   //Rest of the proxy
}

作者在书中还提出了所谓的代理链(Proxy Chaining)技术,实质上就是使得分别实现不同层级接口的代理类形成一个IS-A的继承关系。如上的定义,就可以使ScientificCalculatorClient继承自SimpleCalculatorClient,而不是继承ClientBase<IScientificCalculator>:
public partial class SimpleCalculatorClient : ClientBase<IScientificCalculator>,
                                              ISimpleCalculator
{
   public int Add(int arg1,int arg2)
   {
      return Channel.Add(arg1,arg2);
   }
   //Rest of the proxy
}

public partial class ScientificCalculatorClient : SimpleCalculatorClient,
                                                  IScientificCalculator
{
   public int Multiply(int arg1,int arg2)
   {
      return Channel.Multiply(arg1,arg2);
   }
   //Rest of the proxy
}

只有这样,如下代码才是正确的:
SimpleCalculatorClient proxy1 = new SimpleCalculatorClient(  );
SimpleCalculatorClient proxy2 = new ScientificCalculatorClient(  );
ScientificCalculatorClient proxy3 = new ScientificCalculatorClient(  );

服务契约的分解与设计

契约分离与接口隔离原则(ISP,Interface Segregation Principle)的基本精神是一致的。ISP原则建议使用多个专门的接口,而不是使用单个接口,这样可以防止接口污染,有利于接口重用。契约分解同样如此,但它还要受到实现契约代价的约束。

书中提供了服务契约的分解准则。“合理的契约分解可以实现深度特化、松散耦合、精细调整以及契约的重用。这些优势有助于改善整个系统。总的来说,契约分解的目的就是使契约包含的操作尽可能少。”

设计面向服务的系统时,需要平衡两个影响系统的因素(参见图2-1)。一个是实现服务契约的代价,一个则是将服务契约合并或集成为一个高内聚应用程序的代价。
 figure-2-1.gif
图2-1  平衡服务的个数与规模

定义服务契约时,还要注意到书中所谓的准属性操作(Property-Like Operation)的使用。一言以蔽之,就是如果涉及到对对象状态的管理(在C#中一般体现为属性),则这样的操作不宜被公开为服务操作。原因在于:“客户端应该只负责调用操作,而由服务去管理服务对象的状态。”

契约查询

要查询契约,首先需要了解元数据的信息,WCF提供了如下的几个辅助类,位于System.ServiceModel.Description命名空间:
public enum MetadataExchangeClientMode
{
   MetadataExchange,
   HttpGet
}
class MetadataSet : ...
{...}
public class ServiceEndpointCollection : Collection<ServiceEndpoint>
{...}

public class MetadataExchangeClient
{
   public MetadataExchangeClient(  );
   public MetadataExchangeClient(Binding mexBinding);
   public MetadataExchangeClient(Uri address, MetadataExchangeClientMode mode);

   public MetadataSet GetMetadata();
   public MetadataSet GetMetadata(EndpointAddress address);
   public MetadataSet GetMetadata(Uri address,MetadataExchangeClientMode mode);
   //More members
}

public abstract class MetadataImporter
{
   public abstract ServiceEndpointCollection ImportAllEndpoints(  );
   //More members
}
public class WsdlImporter : MetadataImporter
{
   public WsdlImporter(MetadataSet metadata);
   //More members
}
public class ServiceEndpoint
{
   public EndpointAddress Address
   {get;set;}
   public Binding Binding
   {get;set;}
   public ContractDescription Contract
   {get;}
   //More members
}
public class ContractDescription
{
   public string Name
   {get;set;}
   public string Namespace
   {get;set;}
   //More members
}

书中提供了元数据的查询方法,同时还实现了一个专门用于操作元数据的MetadataHelper类。
public static class MetadataHelper
{
   public static bool QueryContract(string mexAddress,Type contractType);
   public static bool QueryContract(string mexAddress,string contractNamespace, string contractName);
   //More members
}

可以为MetadataHelper类提供我们希望查询的契约类型,或者提供该契约的名称与命名空间:

string address = "...";
bool contractSupported = MetadataHelper.QueryContract(address,typeof(IMyContract));

具体的实现可以参见书中的描述,完整的实现代码可以到作者的网站(http://www.idesign.net)去下载。

 

WCF配置文件全攻略

当今的软件系统开发,如果没有配置文件几乎是不可想象的事。没有配置文件,软件如何按照用户的要求对功能进行定制?没有配置文件,在对软件进行配置安装的时候如何根据具体的硬软件环境进行修改?最重要的是没有配置...
  • wangboxian
  • wangboxian
  • 2014年05月19日 15:20
  • 2139

《The C Programming Language》读书笔记总结 <一>.基础篇

写了这么多年的C代码,回过头来再看《The C Programming Language》这本书,作者Brian W. Kernighan和C语言之父Dennis M. Ritchie。感觉里面的知识...
  • Eastmount
  • Eastmount
  • 2015年10月21日 16:14
  • 2080

[置顶]iOS开发-多线程开发之线程安全篇

前言:一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件和同一个方法等。因此当多个线程访问同一块资源时,很容易会发生数据错误及数据不安...
  • hatsuna
  • hatsuna
  • 2015年07月27日 10:51
  • 530

CS231n 课程笔记翻译

【本文转载自智能单元 - 知乎专栏 https://zhuanlan.zhihu.com/intelligentunit,仅用于学习目的,如有侵权,请联系741258749@qq.com删除】...
  • u011482783
  • u011482783
  • 2016年11月15日 14:37
  • 814

《Programming in Lua 3》读书笔记(十五)

日期:2014.7.22 PartⅢ     The Standard Libraries 19 The Bitwise Library 位运算库 lua中的位运算由于数值类型...
  • u011894560
  • u011894560
  • 2014年08月10日 21:31
  • 4051

WCF 入门教程一(动手新建第一个WCF程序并部署)

WCF的相关概念信息就不在此赘述了,网上一搜一大把。 现在让我们动手搭建我们的第一个wcf程序吧,具体流程如下: 1. 新建立空白解决方案,并在解决方案中新建项目,项目类型为:WCF服务应用程序。 ...
  • xunzaosiyecao
  • xunzaosiyecao
  • 2015年03月07日 20:15
  • 3757

CS231n课程笔记翻译系列之目录汇总

知乎上CS231n课程翻译系列 翻译的笔记非常好,为了方便查看,这里把所有目录列于此,并给出链接。 Python Numpy教程(全篇) Python 基本数据类型 容器(列表, 字典, 集合, ...
  • NNNNNNNNNNNNY
  • NNNNNNNNNNNNY
  • 2017年01月16日 20:20
  • 8427

RCNN学习笔记(7):Faster R-CNN 英文论文翻译笔记

Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks Shaoqing Ren, Kaimin...
  • u011534057
  • u011534057
  • 2016年04月27日 11:51
  • 15108

Services 翻译第五集

原文地址:http://blog.csdn.net/wcs542882916 android官方原文地址http://developer.android.com/guide/components...
  • wcs542882916
  • wcs542882916
  • 2015年05月30日 12:40
  • 247

Services 翻译第二集

原文地址:http://blog.csdn.net/wcs542882916 android官方原文地址http://developer.android.com/guide/components...
  • wcs542882916
  • wcs542882916
  • 2015年05月25日 11:27
  • 222
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Programming WCF Services翻译笔记(四)
举报原因:
原因补充:

(最多只允许输入30个字)