首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

契约的继承

服务契约接口支持继承功能,我们可以定义一个契约层级。但是,ServiceContract特性却是不能继承的:

AttributeUsage(Inherited = false,...)

public sealed class ServiceContractAttribute : Attribute

{...}

因此,接口层级中的每级接口都必须显式的标记ServiceContract特性,如例2-3所示。

例 2-3:服务端契约层级

[ServiceContract]

interface ISimpleCalculator

{

   [OperationContract

   int Add(int arg1,int arg2);

}

[ServiceContract]

interface IScientificCalculator : ISimpleCalculator

{

   [OperationContract]

   int Multiply(int arg1,int arg2);

}

至于一个契约层级的实现,一个单独的服务类能够实现整个契约层级,这与经典的C#编程完全一致:

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"

   />

</service>

客户端契约层级

当客户端导入一个服务终结点的元数据时,如果该终结点的契约属于接口层级的一部分,则生成的客户端契约将不再维持原来的层级关系。相反,它会取消层级,组成一个单独的契约,名称为终结点的契约名。这个单独的契约包含了层级中从上至下所有接口定义的操作。然而,如果使用OperationContract特性中的Action与ResponseAction属性,那么导入的接口定义仍然可以保留原来定义每个操作的契约名。

[AttributeUsage(AttributeTargets.Method)]

public sealed class OperationContractAttribute : Attribute

{

   public string Action

   {get;set;}

   public string ReplyAction

   {get;set;}

   //更多成员

}

最后,一个单独的代理类可以实现导入契约的所有方法。如果给定例2-3的定义,导入的契约以及生成的代理类如例2-4所示。

例2-4:取消层级关系的客户端定义

[ServiceContract]

public interface IScientificCalculator

{

   [OperationContract(Action = ".../ISimpleCalculator/Add",

                      ReplyAction = ".../ISimpleCalculator/AddResponse")]

   int Add(int arg1,int arg2);

   [OperationContract(Action = ".../IScientificCalculator/Multiply"

                      ReplyAction = ".../IScientificCalculator/MultiplyResponse")]

   int Multiply(int arg1,int arg2);

}

public partial class ScientificCalculatorClient :

             ClientBase<IScientificCalculator>, IScientificCalculator

{

   public int Add(int arg1,int arg2)

   {...}

   public int Multiply(int arg1,int arg2)

   {...}

   //代理的其余内容

}

恢复客户端层级

客户端可以手工修改代理以及导入契约的定义,恢复契约层级,如例2-5所示。

例2-5:客户端契约层级

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

   }

   //代理的其余内容

}

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

   }

   //代理的其余内容

}

在不同的操作上使用Action属性值,客户端可以分解服务契约层级中合成契约的定义,提供接口与代理的定义。如例2-5中的ISimpleCalculator和SimpleCalculatorClient。在该例中,并不需要设置Action和ResponseAction属性值,我们完全可以移除它们。然后,手动地将接口添加到客户端所需要的接口链中:

[ServiceContract]

public interface IScientificCalculator : ISimpleCalculator

{...}

尽管服务可能已经为层级中最底层的接口公开了一个单独的终结点,客户端仍然可以将它看作是相同地址的不同终结点,每个终结点对应契约层级的不同层:

<client>

   <endpoint name = "SimpleEndpoint"

      address  = "http://localhost:8001/MyCalculator/"

      binding  = "basicHttpBinding"

      contract = "ISimpleCalculator"

   />

   <endpoint name = "ScientificEndpoint"

      address  = "http://localhost:8001/MyCalculator/"

      binding  = "basicHttpBinding"

      contract = "IScientificCalculator"

   />

</client>

现在,客户端可以编写如下代理,充分地利用契约层级的优势:

SimpleCalculatorClient proxy1 = new SimpleCalculatorClient();

proxy1.Add(1,2);

proxy1.Close();

ScientificCalculatorClient proxy2 = new ScientificCalculatorClient();

proxy2.Add(3,4);

proxy2.Multiply(5,6);

proxy2.Close();

例2-5对代理的分解,解除了契约中每一层级之间的依赖,实现了契约层级的解耦。在客户端代码中,凡是希望使用ISimpleCalculator引用的,都可以指派为IScientificCalculator类型的引用:

void UseCalculator(ISimpleCalculator calculator)

{...}

ISimpleCalculator proxy1 = new SimpleCalculatorClient();

ISimpleCalculator proxy2 = new ScientificCalculatorClient();

IScientificCalculator  proxy3 = new ScientificCalculatorClient();

SimpleCalculatorClient  proxy4 = new SimpleCalculatorClient();

ScientificCalculatorClient proxy5 = new ScientificCalculatorClient();

UseCalculator(proxy1);

UseCalculator(proxy2);

UseCalculator(proxy3);

UseCalculator(proxy4);

UseCalculator(proxy5);

但是,代理之间并不存在IS-A关系。即使IScientificCalculator接口派生自ISimpleCalculator接口,也不能认为代理类ScientificCalculatorClient就是SimpleCalculatorClient类型。此外,我们必须为子契约重复实现代理中的基契约。调整的办法是使用所谓的代理链(Proxy Chaining)技术,如例2-6所示。

例2-6:代理链

public partial class SimpleCalculatorClient : ClientBase<IScientificCalculator>,

                                              ISimpleCalculator

{

   public int Add(int arg1,int arg2)

   {

      return Channel.Add(arg1,arg2);

   }

   //代理的其余内容

}

public partial class ScientificCalculatorClient : SimpleCalculatorClient,

                                                  IScientificCalculator

{

   public int Multiply(int arg1,int arg2)

   {

      return Channel.Multiply(arg1,arg2);

   }

   //代理的其余内容

}

只有实现了最顶层的基契约的代理直接继承于ClientBase<T>,提供的类型参数则为最底层的子接口类型。所有的其他代理则直接继承于它们的上一级代理,同时实现各自的契约接口。

代理链为代理建立了IS-A关系,保证了代码的重用。在客户端代码中,凡是希望使用SimpleCalculatorClient引用的,都可以指派为ScientificCalculatorClient类型的引用:

void UseCalculator(SimpleCalculatorClient calculator)

{...}

SimpleCalculatorClient proxy1 = new SimpleCalculatorClient();

SimpleCalculatorClient proxy2 = new ScientificCalculatorClient();

ScientificCalculatorClient proxy3 = new ScientificCalculatorClient();

UseCalculator(proxy1);

UseCalculator(proxy2);

UseCalculator(proxy3);

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论