3.5.1 绑定元素(Binding Element)
绑定元素,顾名思义就是构成一个绑定对象的元素。绑定对象最根本的目的就是创建信道栈,借此实现对消息的传输、编码和其他消息处理功能,比如安全、可靠传输、事务流转等。组成信道栈的单个信道对象基于对某项单一消息处理功能的实现,在不同环境中,可以按照具体的需求选择所需的信道,并根据一定的顺序对其重组。一般来说,绑定元素的组合方式,决定了绑定对象最终创建的信道栈。绑定元素的重组决定了信道的重组。
关于绑定元素
一个绑定对象由一系列绑定元素组成,一般来讲,每个绑定元素负责创建相应的信道。所以绑定元素集合的构成及它们之间的先后顺序,决定了最终生成的信道栈中的信道组成,以及它们位于栈中的先后顺序。WCF之所以在设计的时候将绑定和绑定元素分离开发,是基于灵活性、可扩展性考虑的。
我们不可能、也没有必要创建一个万能的信道以提供消息交换中的所有功能,所以我们让一个信道只承载某个单一的功能,比如传输信道专注于网络传输,压缩信道专注于消息的压缩和解压缩。WCF还定义了一系列的信道,它们分别专注于安全、可靠传输和事务流转等。这种信道组合的设计方式使得我们可以根据具体的需求来定制将要创建的信道栈,让它只保留必需的功能,去除不必要的功能。
同理,我们可以根据具体实际需求,将必要的绑定元素进行有序的重组,从而创建最能适合具体通信场景的绑定。由于信道可以分为必须的传输信道、消息编码信道和可选的基于某种WS-*协议实现的协议信道,与之相对,绑定元素可以分为传输绑定元素、消息编码绑定元素和协议绑定元素。
注: 并非所有绑定元素都会从事信道的创建。实际上WCF中并不存在消息编码信道,消息编码绑定元素,仅仅是传递一些编码相关的绑定参数给绑定上下文,以便传输绑定元素选择相应的消息编码方式。在第6章会对编码的实现进行详细的介绍,在这里主要是对整个绑定模型进行介绍,所以还是采用了“编码信道”这一说法。
绑定元素的最根本功能就是实现对信道监听器和信道工厂的创建。这可以从所有绑定元素的基类, System.ServiceModel.Channels.BindingElement的定义上看出来。
public abstract class BindingElement { protected BindingElement(); protected BindingElement(BindingElement elementToBeCloned); public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel> (BindingContext context); public virtual IChannelListener<TChannel> BuildChannelListener<TChannel> (BindingContext context) where TChannel : class, IChannel; public virtual bool CanBuildChannelFactory<TChannel>(BindingContext context); public virtual bool CanBuildChannelListener<TChannel>(BindingContext context) where TChannel : class, IChannel; public abstract BindingElement Clone(); public abstract T GetProperty<T>(BindingContext context) where T : class; }
BindingElement的核心方法成员有两个:BuildChannelListener<TChannel>和Build-ChannelFactory<TChannel>,用于创建相应的信道监听器和信道工厂。两个Build方法的参数都是BindingContext。而CanBuildChannelFactory<TChannel>和CanBuildChannel- Listener<TChannel>则属于两个测试性质的方法,用于检验相应的信道监听器和信道功能是否可以被创建。
案例演示:如何自定义绑定元素
在上面的案例中,我们自定义了两种类型的信道管理器:数据报信道管理器和会话信道信道管理器,它们又包含各自的信道监听器和信道工厂。在这里须要为它们分别创建绑定元素。
1.创建数据报绑定元素
针对上面创建的两个数据报信道管理器:SimpleDatagramChannelFactory和SimpleDatagramChannelListener,创建了相应绑定元素SimpleDatagramBindingElement。SimpleDatagramChannelFactory和SimpleDatagramChannelListener的创建分别实现在两个被重写的方法:BuildChannelFactory<TChannel>和BuildChannelListener<TChannel>中。此外还重写了两个额外的方法:Clone和GetProperty<T>,前者用于克隆一个新的绑定元素,后一个和定义在信道、信道管理器的同名方法一样,用于获取基于某种类型的属性。
public class SimpleDatagramBindingElement : BindingElement {
public SimpleDatagramBindingElement() { PrintHelper.Print(this, "SimpleDatagramBindingElement"); } public override BindingElement Clone() { PrintHelper.Print(this, "Clone"); return new SimpleDatagramBindingElement(); } public override T GetProperty<T>(BindingContext context) { PrintHelper.Print(this, string.Format("GetProperty<{0}>", typeof(T) .Name)); return context.GetInnerProperty<T>(); } public override IChannelFactory<TChannel> BuildChannelFactory<TChannel> (BindingContext context) { PrintHelper.Print(this, "BuildChannelFactory<TChannel>"); return new SimpleDatagramChannelFactory<TChannel>(context) as IChannelFactory<TChannel>; } public override IChannelListener<TChannel> BuildChannelListener <TChannel>(BindingContext context) { PrintHelper.Print(this, "BuildChannelListener<TChannel>"); return new SimpleDatagramChannelListener<TChannel>(context) as IChannelListener<TChannel>; } }
2.创建会话绑定元素
按照与创建SimpleDatagramBindingElement相同的方式,为先前创建的会话信道管理器(SimpleSessionChannelListener和SimpleSessionChannelFactory)建立相应的绑定元素:SimpleSessionBindingElement。
using System.ServiceModel.Channels; namespace Artech.CustomChannels { public class SimpleSessionBindingElement : BindingElement { public override BindingElement Clone() { return new SimpleSessionBindingElement(); } public override T GetProperty<T>(BindingContext context) { return context.GetInnerProperty<T>(); } public override IChannelFactory<TChannel> BuildChannelFactory <TChannel>(BindingContext context) { PrintHelper.Print(this, "BuildChannelFactory<TChannel>"); return new SimpleSessionChannelFactory<TChannel>(context) as IChannelFactory<TChannel>; } public override IChannelListener<TChannel> BuildChannelListener <TChannel>(BindingContext context) { PrintHelper.Print(this, "BuildChannelListener<TChannel>"); return new SimpleSessionChannelListener<TChannel>(context) as IChannelListener<TChannel>; } } }