契约的继承
服务契约接口支持继承功能,我们可以定义一个契约层级。但是,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);





