博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
服务契约
阅读量:6983 次
发布时间:2019-06-27

本文共 5471 字,大约阅读时间需要 18 分钟。

《Programming WCF Services》翻译笔记之二

本书的第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)。一个是实现服务契约的代价,一个则是将服务契约合并或集成为一个高内聚应用程序的代价。

图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));

 

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

本文转自wayfarer51CTO博客,原文链接:http://blog.51cto.com/wayfarer/280115,如需转载请自行联系原作者

你可能感兴趣的文章
今年光伏市场规模可达30GW 分布式有望占据三分江山
查看>>
因新漏洞问题 Firefox 49发布时间将延期一周
查看>>
WLAN产品形态之分层架构
查看>>
《敏捷可执行需求说明 Scrum提炼及实现技术》—— 1.2 识别不确定性的影响
查看>>
Chrome 隐藏 SSL 证书信息 禁止禁用 DRM
查看>>
《Windows Server 2012 Hyper-V虚拟化管理实践》——3.2 Hyper-V主机日常管理
查看>>
《C语言编程魔法书:基于C11标准》——第一篇 预备知识篇 第1章 C魔法概览1.1 例说编程语言...
查看>>
《IPv6安全》——1.7 推荐读物和资料
查看>>
《实施Cisco统一通信管理器(CIPT2)》一1.2 概述部署多站点环境时将会遇到的挑战...
查看>>
《Photoshop Lab修色圣典(修订版)》—第1课深入讨论
查看>>
Chromium 操作系统即将支持所有 SBC 单板电脑
查看>>
《CCNP安全Secure 642-637认证考试指南》——第8章 配置与实施路由式数据面安全...
查看>>
Remix OS PC 版面向全球开放
查看>>
Debian GNU/Linux 9 将切换至 GCC6 编译器
查看>>
《VMware 网络技术:原理与实践》—— 3.2 以太网
查看>>
《程序员的修炼——从优秀到卓越》一一1.6 勿以专家自居
查看>>
《Adobe Illustrator CS5中文版经典教程》—第0课0.5节使用绘图模式
查看>>
AngularJS 的自定义指令
查看>>
《CCNA ICND2(200-101)认证考试指南(第4版)》——第1章定义生成树协议
查看>>
什么样的 RPC 才是好用的 RPC
查看>>