3.6.2 自定义绑定
在大部分WCF应用中,使用上面介绍的系统自定义绑定类型大都能够解决我们的问题。如果现有的绑定实在不能满足你的需求,还可以采用编程或配置的方式自定义绑定。绑定的本质就是由一组绑定元素组成的有序集合,而自定义绑定的本质在于根据具体的需要对绑定元素进行重组。
通过编程的方式自定义绑定
在System.ServiceModel.Channels.命名空间下,定义了一个CustomBinding类,可以直接创建CustomBinding对象,也可以自定义继承自CustomBinding的绑定类。CustomBinding定义一个一系列的重载构造函数,可以利用这些构造函数直接设置需要的绑定元素(通过bindingElementsInTopDownChannelStackOrder参数);也可以通过配置的方式设置绑定元素,然后通过配置节名称创建CustomBinding对象。甚至可以借助于一个存在的绑定实例创建CustomBinding对象。
public class CustomBinding : Binding { public CustomBinding(); public CustomBinding(Binding binding); public CustomBinding(params BindingElement[] bindingElementsInTopDownChannelStackOrder); public CustomBinding(IEnumerable<BindingElement> bindingElementsInTopDownChannelStackOrder); public CustomBinding(string configurationName); public CustomBinding(string name, string ns, params BindingElement[] bindingElementsInTopDownChannelStackOrder); public override BindingElementCollection CreateBindingElements(); public BindingElementCollection Elements { get; } public override string Scheme { get; } }
下面的代码模拟了通过CustomBinding对象进行服务的寄宿。首先构建了一个BindingElement数组,其中添加了3个绑定元素,第一个是在前面的案例中创建的自定义绑定元素:SimpleDatagramBindingElement,其余两个是消息编码绑定元素和传输绑定元素:TextMessageEncodingBindingElement和HttpTransportBindingElement。由此绑定元素数组创建CustomBinding对象,最终添加到终结点中。
class Program { static void Main(string[] args) { BindingElement[] elements = new BindingElement[] {new SimpleDatagramBindingElement(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement()}; CustomBinding binding = new CustomBinding(elements); using (ServiceHost host = new ServiceHost(typeof(Service))) { host.AddServiceEndpoint(typeof(IService), binding, "http://127.0.0.1:9999/service"); host.Open(); Console.Read(); } } }
同理在客户端,也可以通过基于CustomBinding对象的终结点进行服务的调用,下面就是一个简单的例子。
class Program { static void Main(string[] args) { BindingElement[] elements = new BindingElement[] { new SimpleDatagramBindingElement(), new TextMessageEncodingBindingElement(), new HttpTransportBindingElement() }; CustomBinding binding = new CustomBinding(elements); EndpointAddress address = new EndpointAddress ("http://127.0.0.1:9999/service"); using (ChannelFactory<IService> channelFactory = new ChannelFactory<IService>(binding, address)) { IService proxy = channelFactory.CreateChannel(); using (proxy as IDisposable) { proxy.DoSomething(); } } Console.Read(); } }
通过配置方式自定义绑定
我们说过自定义绑定的本质就是根据具体的需求重组绑定元素,通过编程的方式可以在创建CustomBinding的时候显式指定绑定元素集合,现在我们介绍如何通过配置的方式进行绑定元素的重组。自定义绑定定义在<bindings>/<customBinding>配置节中,可以在其中定义若干具有不同绑定元素组合的自定义绑定,并为每个配置的绑定起一个唯一的名称。对于WCF预定义的绑定元素,只须直接将其配置到绑定配置节中的绑定元素列表之中。
在如下的配置中,定义了一个名称为自定义MyCustomBinding绑定,该绑定采用基于HTTP的传输,基于文本的消息编码。此外添加了<reliableSession />配置节使其支持可靠会话,<transactionFlow/>使其支持事务流转。在具体的服务终结点配置中,通过bindingConfiguration="MyCustomBinding”实现对该自定义绑定的引用。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <bindings> <customBinding> <binding name="MyCustomBinding"> <textMessageEncoding /> <transactionFlow /> <reliableSession /> <httpTransport /> </binding> </customBinding> </bindings> <services> <service name="Artech.CustomChannels.Services.Service"> <endpoint address="http://127.0.0.1:9999/service" binding= "customBinding" bindingConfiguration="MyCustomBinding" contract="Artech.CustomChannels.Contracts.IService" /> </service> </services> </system.serviceModel> </configuration>
上面说的是基于预定义绑定元素的添加,那么对于我们自定义的绑定元素又将采用怎样的方式进行配置呢?这涉及一个额外的类型:System.ServiceModel.Configuration.BindingElementExtensionElement。实际上,无论是系统预定义绑定元素,还是自定义绑定元素,都具有相应的继承自BindingElementExtensionElement的类型与之匹配。BindingElementExtensionElement最终继承自System.Configuration.ConfigurationElement,用于定义绑定元素相关的配置信息。如果你要求自定义绑定元素可配置的话,首先要为之定义相应的BindingElementExtensionElement类型。下面是BindingElementExtensionElement的简单定义。
public abstract class BindingElementExtensionElement : ServiceModelExtensionElement { // Methods protected BindingElementExtensionElement(); public virtual void ApplyConfiguration(BindingElement bindingElement); protected internal abstract BindingElement CreateBindingElement(); protected internal virtual void InitializeFrom(BindingElement bindingElement); // Properties public abstract Type BindingElementType { get; } }
BindingElementExtensionElement定义了一个抽象方法CreateBindingElement和一个抽象的属性BindingElementType,分别返回创建的BindingElement对象和BindingElement类型。一般来说,当你为某个自定义绑定元素定义BindingElementExtensionElement类的时候,仅仅实现这两个抽象成员就可以了。在前面的案例中,我们创建了一个自定义的绑定元素:SimpleDatagramBindingElement,现在就为它创建一个BindingElementExtensionElement类型:SimpleDatagramBindingExtensionElement。
namespace Artech.CustomChannels { class SimpleDatagramBindingExtensionElement:BindingElementExtensionElement { public override Type BindingElementType { get { return typeof(SimpleDatagramBindingElement); } } protected override BindingElement CreateBindingElement() { return new SimpleDatagramBindingElement(); } } }
自定义的BindingElementExtensionElement配置到<extensions>/<bindingElement-Extensions>的绑定元素扩展列表中。比如在下面的配置中,我们将上面创建的Simple-DatagramBindingExtensionElement类型添加到<extensions>/<bindingElementExtensions>配置节中,并将其命名为SimpleDatagramBindingExtension,那么SimpleDatagramBindingExtension就可以像预定义的绑定元素扩展一样被添加到自定义的绑定元素列表中了。
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <extensions> <bindingElementExtensions> <add name="SimpleDatagramBindingExtension" type="Artech. CustomChannels.SimpleDatagramBindingExtensionElement, Artech.CustomChannels, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </bindingElementExtensions> </extensions> <bindings> <customBinding> <binding name="MyCustomBinding"> <textMessageEncoding /> <SimpleDatagramBindingExtension /> <httpTransport /> </binding> </customBinding> </bindings> <services> <service name="Artech.CustomChannels.Services.Service"> <endpoint address="http://127.0.0.1:9999/service" binding= "customBinding" bindingConfiguration="MyCustomBinding" contract="Artech.CustomChannels.Contracts.IService" /> </service> </services> </system.serviceModel> </configuration>