绑定类型是开发人员控制WCF程序与其他消息交互的主要手段。从功能上看,绑定创建了通道工厂或通道侦听器的堆栈对象。在设计模式中,一个绑定就是一个工厂。在服务模型层和通道层中,绑定在服务模型层是可见的,它创建的对象作用与通道层。
绑定对象模型
所有的绑定类型都继承自抽象类型System.ServiceModel.Channels.Binding,因此,所有的绑定都具有相同的特性。与通道工厂、通道侦听器和通道不同,绑定类型没有复杂的类型层次关系。绑定类型定义如下:
// System.ServiceModel.Channels.Binding
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
[__DynamicallyInvokable]
public abstract class Binding : IDefaultCommunicationTimeouts
{
private TimeSpan closeTimeout = ServiceDefaults.CloseTimeout;
private string name;
private string namespaceIdentifier;
private TimeSpan openTimeout = ServiceDefaults.OpenTimeout;
private TimeSpan receiveTimeout = ServiceDefaults.ReceiveTimeout;
private TimeSpan sendTimeout = ServiceDefaults.SendTimeout;
internal const string DefaultNamespace = "http://tempuri.org/";
[DefaultValue(typeof(TimeSpan), "00:01:00")]
[__DynamicallyInvokable]
public TimeSpan CloseTimeout
{
[__DynamicallyInvokable]
get
{
return this.closeTimeout;
}
[__DynamicallyInvokable]
set
{
if (value < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRange0")));
}
if (TimeoutHelper.IsTooLarge(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRangeTooBig")));
}
this.closeTimeout = value;
}
}
[__DynamicallyInvokable]
public string Name
{
[__DynamicallyInvokable]
get
{
if (this.name == null)
{
this.name = base.GetType().Name;
}
return this.name;
}
[__DynamicallyInvokable]
set
{
if (string.IsNullOrEmpty(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("value", SR.GetString("SFXBindingNameCannotBeNullOrEmpty"));
}
this.name = value;
}
}
[__DynamicallyInvokable]
public string Namespace
{
[__DynamicallyInvokable]
get
{
return this.namespaceIdentifier;
}
[__DynamicallyInvokable]
set
{
if (value == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
}
if (value.Length > 0)
{
NamingHelper.CheckUriProperty(value, "Namespace");
}
this.namespaceIdentifier = value;
}
}
[DefaultValue(typeof(TimeSpan), "00:01:00")]
[__DynamicallyInvokable]
public TimeSpan OpenTimeout
{
[__DynamicallyInvokable]
get
{
return this.openTimeout;
}
[__DynamicallyInvokable]
set
{
if (value < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRange0")));
}
if (TimeoutHelper.IsTooLarge(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRangeTooBig")));
}
this.openTimeout = value;
}
}
[DefaultValue(typeof(TimeSpan), "00:10:00")]
[__DynamicallyInvokable]
public TimeSpan ReceiveTimeout
{
[__DynamicallyInvokable]
get
{
return this.receiveTimeout;
}
[__DynamicallyInvokable]
set
{
if (value < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRange0")));
}
if (TimeoutHelper.IsTooLarge(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRangeTooBig")));
}
this.receiveTimeout = value;
}
}
[__DynamicallyInvokable]
public abstract string Scheme
{
[__DynamicallyInvokable]
get;
}
[__DynamicallyInvokable]
public MessageVersion MessageVersion
{
[__DynamicallyInvokable]
get
{
return this.GetProperty<MessageVersion>(new BindingParameterCollection());
}
}
[DefaultValue(typeof(TimeSpan), "00:01:00")]
[__DynamicallyInvokable]
public TimeSpan SendTimeout
{
[__DynamicallyInvokable]
get
{
return this.sendTimeout;
}
[__DynamicallyInvokable]
set
{
if (value < TimeSpan.Zero)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRange0")));
}
if (TimeoutHelper.IsTooLarge(value))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString("SFxTimeoutOutOfRangeTooBig")));
}
this.sendTimeout = value;
}
}
[__DynamicallyInvokable]
protected Binding()
{
this.name = null;
this.namespaceIdentifier = "http://tempuri.org/";
}
[__DynamicallyInvokable]
protected Binding(string name, string ns)
{
if (string.IsNullOrEmpty(name))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("name", SR.GetString("SFXBindingNameCannotBeNullOrEmpty"));
}
if (ns == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("ns");
}
if (ns.Length > 0)
{
NamingHelper.CheckUriParameter(ns, "ns");
}
this.name = name;
this.namespaceIdentifier = ns;
}
[__DynamicallyInvokable]
public IChannelFactory<TChannel> BuildChannelFactory<TChannel>(params object[] parameters)
{
return this.BuildChannelFactory<TChannel>(new BindingParameterCollection(parameters));
}
[__DynamicallyInvokable]
public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingParameterCollection parameters)
{
this.EnsureInvariants();
BindingContext bindingContext = new BindingContext(new CustomBinding(this), parameters);
IChannelFactory<TChannel> channelFactory = bindingContext.BuildInnerChannelFactory<TChannel>();
bindingContext.ValidateBindingElementsConsumed();
this.ValidateSecurityCapabilities(channelFactory.GetProperty<ISecurityCapabilities>(), parameters);
return channelFactory;
}
private void ValidateSecurityCapabilities(ISecurityCapabilities runtimeSecurityCapabilities, BindingParameterCollection parameters)
{
ISecurityCapabilities property = this.GetProperty<ISecurityCapabilities>(parameters);
if (SecurityCapabilities.IsEqual(property, runtimeSecurityCapabilities))
{
return;
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SecurityCapabilitiesMismatched", this)));
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(params object[] parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(new BindingParameterCollection(parameters));
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, params object[] parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(listenUriBaseAddress, new BindingParameterCollection(parameters));
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, params object[] parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(listenUriBaseAddress, listenUriRelativeAddress, new BindingParameterCollection(parameters));
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, params object[] parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(listenUriBaseAddress, listenUriRelativeAddress, listenUriMode, new BindingParameterCollection(parameters));
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingParameterCollection parameters) where TChannel : class, IChannel
{
UriBuilder uriBuilder = new UriBuilder(this.Scheme, DnsCache.MachineName);
return this.BuildChannelListener<TChannel>(uriBuilder.Uri, string.Empty, ListenUriMode.Unique, parameters);
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, BindingParameterCollection parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(listenUriBaseAddress, string.Empty, ListenUriMode.Explicit, parameters);
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, BindingParameterCollection parameters) where TChannel : class, IChannel
{
return this.BuildChannelListener<TChannel>(listenUriBaseAddress, listenUriRelativeAddress, ListenUriMode.Explicit, parameters);
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(Uri listenUriBaseAddress, string listenUriRelativeAddress, ListenUriMode listenUriMode, BindingParameterCollection parameters) where TChannel : class, IChannel
{
this.EnsureInvariants();
BindingContext bindingContext = new BindingContext(new CustomBinding(this), parameters, listenUriBaseAddress, listenUriRelativeAddress, listenUriMode);
IChannelListener<TChannel> channelListener = bindingContext.BuildInnerChannelListener<TChannel>();
bindingContext.ValidateBindingElementsConsumed();
this.ValidateSecurityCapabilities(channelListener.GetProperty<ISecurityCapabilities>(), parameters);
return channelListener;
}
[__DynamicallyInvokable]
public bool CanBuildChannelFactory<TChannel>(params object[] parameters)
{
return this.CanBuildChannelFactory<TChannel>(new BindingParameterCollection(parameters));
}
[__DynamicallyInvokable]
public virtual bool CanBuildChannelFactory<TChannel>(BindingParameterCollection parameters)
{
BindingContext bindingContext = new BindingContext(new CustomBinding(this), parameters);
return bindingContext.CanBuildInnerChannelFactory<TChannel>();
}
public bool CanBuildChannelListener<TChannel>(params object[] parameters) where TChannel : class, IChannel
{
return this.CanBuildChannelListener<TChannel>(new BindingParameterCollection(parameters));
}
public virtual bool CanBuildChannelListener<TChannel>(BindingParameterCollection parameters) where TChannel : class, IChannel
{
BindingContext bindingContext = new BindingContext(new CustomBinding(this), parameters);
return bindingContext.CanBuildInnerChannelListener<TChannel>();
}
[__DynamicallyInvokable]
public abstract BindingElementCollection CreateBindingElements();
[__DynamicallyInvokable]
public T GetProperty<T>(BindingParameterCollection parameters) where T : class
{
BindingContext bindingContext = new BindingContext(new CustomBinding(this), parameters);
return bindingContext.GetInnerProperty<T>();
}
private void EnsureInvariants()
{
this.EnsureInvariants(null);
}
internal void EnsureInvariants(string contractName)
{
BindingElementCollection bindingElementCollection = this.CreateBindingElements();
TransportBindingElement transportBindingElement = null;
int i;
for (i = 0; i < bindingElementCollection.Count; i++)
{
transportBindingElement = (((Collection<BindingElement>)bindingElementCollection)[i] as TransportBindingElement);
if (transportBindingElement != null)
{
break;
}
}
if (transportBindingElement == null)
{
if (contractName == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("CustomBindingRequiresTransport", this.Name)));
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("SFxCustomBindingNeedsTransport1", contractName)));
}
if (i != bindingElementCollection.Count - 1)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("TransportBindingElementMustBeLast", this.Name, transportBindingElement.GetType().Name)));
}
if (string.IsNullOrEmpty(transportBindingElement.Scheme))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("InvalidBindingScheme", transportBindingElement.GetType().Name)));
}
if (this.MessageVersion != null)
{
return;
}
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("MessageVersionMissingFromBinding", this.Name)));
}
internal void CopyTimeouts(IDefaultCommunicationTimeouts source)
{
this.CloseTimeout = source.CloseTimeout;
this.OpenTimeout = source.OpenTimeout;
this.ReceiveTimeout = source.ReceiveTimeout;
this.SendTimeout = source.SendTimeout;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeName()
{
return this.Name != base.GetType().Name;
}
[EditorBrowsable(EditorBrowsableState.Never)]
public bool ShouldSerializeNamespace()
{
return this.Namespace != "http://tempuri.org/";
}
}
BindingElement类型
BindingElement是一个工厂对象。更确切地说,BindingElement类型定义了返回通道工厂和通道侦听器的方法。创建BindingElement对象的主要方式是通过Binding.CreateBindingElements方法。对于通道工厂、通道侦听器和通道,没有万能的BindingElement类型。WCF类型系统包含许多BindingElement的子类型,而每个都表示WCF支持的消息功能。BindingElement类型的定义如下所示:
// System.ServiceModel.Channels.BindingElement
using System.ServiceModel;
using System.ServiceModel.Channels;
[__DynamicallyInvokable]
public abstract class BindingElement
{
[__DynamicallyInvokable]
protected BindingElement()
{
}
[__DynamicallyInvokable]
protected BindingElement(BindingElement elementToBeCloned)
{
}
[__DynamicallyInvokable]
public abstract BindingElement Clone();
[__DynamicallyInvokable]
public virtual IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context)
{
if (context == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
}
return context.BuildInnerChannelFactory<TChannel>();
}
public virtual IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context) where TChannel : class, IChannel
{
if (context == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
}
return context.BuildInnerChannelListener<TChannel>();
}
[__DynamicallyInvokable]
public virtual bool CanBuildChannelFactory<TChannel>(BindingContext context)
{
if (context == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
}
return context.CanBuildInnerChannelFactory<TChannel>();
}
public virtual bool CanBuildChannelListener<TChannel>(BindingContext context) where TChannel : class, IChannel
{
if (context == null)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context");
}
return context.CanBuildInnerChannelListener<TChannel>();
}
[__DynamicallyInvokable]
public abstract T GetProperty<T>(BindingContext context) where T : class;
internal T GetIndividualProperty<T>() where T : class
{
return this.GetProperty<T>(new BindingContext(new CustomBinding(), new BindingParameterCollection()));
}
internal virtual bool IsMatch(BindingElement b)
{
return false;
}
}
BindingElement的查询机制
BindingElement的查询机制和通道、通道工厂及通道侦听器一样。BindingElement的查询机制依赖于BindingContext中对下一个BindingElement对象的代理查询。下面就是BindingElement子类型查询机制的实现,这里展示了如何使用BindingContext参数委托查询:
public override T GetProperty<T>(BindingContext context)
{
if(context == null)
{
throw new ArgumentNullException("context");
}
//委托所有的查询,除了其他绑定的一些查询功能
if (typeof(T) != typeof(SomeCapabilility))
{
//通过BindingContext利用其他绑定元素的查询机制
return context.GetInnerProperty<T>();
}
//BindingElement元素里的一个域成员可以放回这个功能
return (T)this.someCapability;
}
TransportBindingElement类型
最难应用到Binding上的规则是,如何让CreateBindingElements返回的BindingElement对象可以创建传输通道工厂或通道侦听器。理论看起来很合理,因为消息终结点只有在支持特定形式的传输时才有意义。由于这个原因,WCF类型系统定义了一个抽象类型System.ServicerModel.Channels.TransportBindingElement。TransportBindingElement类型定义了几个只有通道工厂或通道侦听器需要的成员,但是TransportBindingElement继承自BindingElement类型。
因为BindingElement集合是创建通道侦听器和通道工厂堆栈的蓝图,所以,TransportBindingElement必须出现在集合的末端。Binding和BindingContext类型都强化了这一规则。
BindingContext类型
绑定和BindingElement对象把绝大部分创建通道工厂堆栈和通道侦听器堆栈的工厂委托给System.ServiceModel.Channels.BindingContext类型来做。当创建、测试或查询通道工厂堆栈或通道侦听器堆栈时,BindingContext类型提供上下文信息给BindingElement集合对象。