//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel.Channels { using System.ComponentModel; using System.Runtime; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Security; using System.Xml; public sealed class ReliableSessionBindingElement : BindingElement, IPolicyExportExtension { TimeSpan acknowledgementInterval = ReliableSessionDefaults.AcknowledgementInterval; bool flowControlEnabled = ReliableSessionDefaults.FlowControlEnabled; TimeSpan inactivityTimeout = ReliableSessionDefaults.InactivityTimeout; int maxPendingChannels = ReliableSessionDefaults.MaxPendingChannels; int maxRetryCount = ReliableSessionDefaults.MaxRetryCount; int maxTransferWindowSize = ReliableSessionDefaults.MaxTransferWindowSize; bool ordered = ReliableSessionDefaults.Ordered; ReliableMessagingVersion reliableMessagingVersion = ReliableMessagingVersion.Default; InternalDuplexBindingElement internalDuplexBindingElement; static MessagePartSpecification bodyOnly; public ReliableSessionBindingElement() { } internal ReliableSessionBindingElement(ReliableSessionBindingElement elementToBeCloned) : base(elementToBeCloned) { this.AcknowledgementInterval = elementToBeCloned.AcknowledgementInterval; this.FlowControlEnabled = elementToBeCloned.FlowControlEnabled; this.InactivityTimeout = elementToBeCloned.InactivityTimeout; this.MaxPendingChannels = elementToBeCloned.MaxPendingChannels; this.MaxRetryCount = elementToBeCloned.MaxRetryCount; this.MaxTransferWindowSize = elementToBeCloned.MaxTransferWindowSize; this.Ordered = elementToBeCloned.Ordered; this.ReliableMessagingVersion = elementToBeCloned.ReliableMessagingVersion; this.internalDuplexBindingElement = elementToBeCloned.internalDuplexBindingElement; } public ReliableSessionBindingElement(bool ordered) { this.ordered = ordered; } [DefaultValue(typeof(TimeSpan), ReliableSessionDefaults.AcknowledgementIntervalString)] public TimeSpan AcknowledgementInterval { get { return this.acknowledgementInterval; } set { if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero))); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } this.acknowledgementInterval = value; } } [DefaultValue(ReliableSessionDefaults.FlowControlEnabled)] public bool FlowControlEnabled { get { return this.flowControlEnabled; } set { this.flowControlEnabled = value; } } [DefaultValue(typeof(TimeSpan), ReliableSessionDefaults.InactivityTimeoutString)] public TimeSpan InactivityTimeout { get { return this.inactivityTimeout; } set { if (value <= TimeSpan.Zero) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.TimeSpanMustbeGreaterThanTimeSpanZero))); } if (TimeoutHelper.IsTooLarge(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig))); } this.inactivityTimeout = value; } } [DefaultValue(ReliableSessionDefaults.MaxPendingChannels)] public int MaxPendingChannels { get { return this.maxPendingChannels; } set { if (value <= 0 || value > 16384) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.ValueMustBeInRange, 0, 16384))); this.maxPendingChannels = value; } } [DefaultValue(ReliableSessionDefaults.MaxRetryCount)] public int MaxRetryCount { get { return this.maxRetryCount; } set { if (value <= 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.ValueMustBePositive))); this.maxRetryCount = value; } } [DefaultValue(ReliableSessionDefaults.MaxTransferWindowSize)] public int MaxTransferWindowSize { get { return this.maxTransferWindowSize; } set { if (value <= 0 || value > 4096) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.ValueMustBeInRange, 0, 4096))); this.maxTransferWindowSize = value; } } [DefaultValue(ReliableSessionDefaults.Ordered)] public bool Ordered { get { return this.ordered; } set { this.ordered = value; } } [DefaultValue(typeof(ReliableMessagingVersion), ReliableSessionDefaults.ReliableMessagingVersionString)] public ReliableMessagingVersion ReliableMessagingVersion { get { return this.reliableMessagingVersion; } set { if (value == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value"); } if (!ReliableMessagingVersion.IsDefined(value)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value")); } this.reliableMessagingVersion = value; } } static MessagePartSpecification BodyOnly { get { if (bodyOnly == null) { MessagePartSpecification temp = new MessagePartSpecification(true); temp.MakeReadOnly(); bodyOnly = temp; } return bodyOnly; } } public override BindingElement Clone() { return new ReliableSessionBindingElement(this); } public override T GetProperty(BindingContext context) { if (context == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); } if (typeof(T) == typeof(ChannelProtectionRequirements)) { ChannelProtectionRequirements myRequirements = this.GetProtectionRequirements(); myRequirements.Add(context.GetInnerProperty() ?? new ChannelProtectionRequirements()); return (T)(object)myRequirements; } else if (typeof(T) == typeof(IBindingDeliveryCapabilities)) { return (T)(object)new BindingDeliveryCapabilitiesHelper(this, context.GetInnerProperty()); } else { return context.GetInnerProperty(); } } ChannelProtectionRequirements GetProtectionRequirements() { // Listing headers that must be signed. ChannelProtectionRequirements result = new ChannelProtectionRequirements(); MessagePartSpecification signedReliabilityMessageParts = WsrmIndex.GetSignedReliabilityMessageParts( this.reliableMessagingVersion); result.IncomingSignatureParts.AddParts(signedReliabilityMessageParts); result.OutgoingSignatureParts.AddParts(signedReliabilityMessageParts); if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005) { // Adding RM protocol message actions so that each RM protocol message's body will be // signed and encrypted. // From the Client to the Service ScopedMessagePartSpecification signaturePart = result.IncomingSignatureParts; ScopedMessagePartSpecification encryptionPart = result.IncomingEncryptionParts; ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.AckRequestedAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.CreateSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.SequenceAcknowledgementAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.LastMessageAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.TerminateSequenceAction); // From the Service to the Client signaturePart = result.OutgoingSignatureParts; encryptionPart = result.OutgoingEncryptionParts; ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.CreateSequenceResponseAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.SequenceAcknowledgementAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.LastMessageAction); ProtectProtocolMessage(signaturePart, encryptionPart, WsrmFeb2005Strings.TerminateSequenceAction); } else if (this.reliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11) { // Adding RM protocol message actions so that each RM protocol message's body will be // signed and encrypted. // From the Client to the Service ScopedMessagePartSpecification signaturePart = result.IncomingSignatureParts; ScopedMessagePartSpecification encryptionPart = result.IncomingEncryptionParts; ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.AckRequestedAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceResponseAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CreateSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.FaultAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.SequenceAcknowledgementAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceResponseAction); // From the Service to the Client signaturePart = result.OutgoingSignatureParts; encryptionPart = result.OutgoingEncryptionParts; ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.AckRequestedAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CloseSequenceResponseAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.CreateSequenceResponseAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.FaultAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.SequenceAcknowledgementAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceAction); ProtectProtocolMessage(signaturePart, encryptionPart, Wsrm11Strings.TerminateSequenceResponseAction); } else { throw Fx.AssertAndThrow("Reliable messaging version not supported."); } return result; } public override IChannelFactory BuildChannelFactory(BindingContext context) { if (context == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); this.VerifyTransportMode(context); this.SetSecuritySettings(context); InternalDuplexBindingElement.AddDuplexFactorySupport(context, ref this.internalDuplexBindingElement); if (typeof(TChannel) == typeof(IOutputSessionChannel)) { if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } else if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } else if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } else if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } } else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) { if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } else if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } } else if (typeof(TChannel) == typeof(IRequestSessionChannel)) { if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } else if (context.CanBuildInnerChannelFactory()) { return (IChannelFactory)(object) new ReliableChannelFactory( this, context.BuildInnerChannelFactory(), context.Binding); } } throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", SR.GetString(SR.ChannelTypeNotSupported, typeof(TChannel))); } public override bool CanBuildChannelFactory(BindingContext context) { if (context == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); InternalDuplexBindingElement.AddDuplexFactorySupport(context, ref this.internalDuplexBindingElement); if (typeof(TChannel) == typeof(IOutputSessionChannel)) { return context.CanBuildInnerChannelFactory() || context.CanBuildInnerChannelFactory() || context.CanBuildInnerChannelFactory() || context.CanBuildInnerChannelFactory(); } else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) { return context.CanBuildInnerChannelFactory() || context.CanBuildInnerChannelFactory(); } else if (typeof(TChannel) == typeof(IRequestSessionChannel)) { return context.CanBuildInnerChannelFactory() || context.CanBuildInnerChannelFactory(); } return false; } public override IChannelListener BuildChannelListener(BindingContext context) { if (context == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); this.VerifyTransportMode(context); this.SetSecuritySettings(context); #pragma warning suppress 56506 // BindingContext guarantees BindingParameters is never null. IMessageFilterTable table = context.BindingParameters.Find>(); InternalDuplexBindingElement.AddDuplexListenerSupport(context, ref this.internalDuplexBindingElement); if (typeof(TChannel) == typeof(IInputSessionChannel)) { ReliableChannelListenerBase listener = null; if (context.CanBuildInnerChannelListener()) { listener = new ReliableInputListenerOverReplySession(this, context); } else if (context.CanBuildInnerChannelListener()) { listener = new ReliableInputListenerOverReply(this, context); } else if (context.CanBuildInnerChannelListener()) { listener = new ReliableInputListenerOverDuplexSession(this, context); } else if (context.CanBuildInnerChannelListener()) { listener = new ReliableInputListenerOverDuplex(this, context); } if (listener != null) { listener.LocalAddresses = table; return (IChannelListener)(object)listener; } } else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) { ReliableChannelListenerBase listener = null; if (context.CanBuildInnerChannelListener()) { listener = new ReliableDuplexListenerOverDuplexSession(this, context); } else if (context.CanBuildInnerChannelListener()) { listener = new ReliableDuplexListenerOverDuplex(this, context); } if (listener != null) { listener.LocalAddresses = table; return (IChannelListener)(object)listener; } } else if (typeof(TChannel) == typeof(IReplySessionChannel)) { ReliableChannelListenerBase listener = null; if (context.CanBuildInnerChannelListener()) { listener = new ReliableReplyListenerOverReplySession(this, context); } else if (context.CanBuildInnerChannelListener()) { listener = new ReliableReplyListenerOverReply(this, context); } if (listener != null) { listener.LocalAddresses = table; return (IChannelListener)(object)listener; } } throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("TChannel", SR.GetString(SR.ChannelTypeNotSupported, typeof(TChannel))); } public override bool CanBuildChannelListener(BindingContext context) { if (context == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); InternalDuplexBindingElement.AddDuplexListenerSupport(context, ref this.internalDuplexBindingElement); if (typeof(TChannel) == typeof(IInputSessionChannel)) { return context.CanBuildInnerChannelListener() || context.CanBuildInnerChannelListener() || context.CanBuildInnerChannelListener() || context.CanBuildInnerChannelListener(); } else if (typeof(TChannel) == typeof(IDuplexSessionChannel)) { return context.CanBuildInnerChannelListener() || context.CanBuildInnerChannelListener(); } else if (typeof(TChannel) == typeof(IReplySessionChannel)) { return context.CanBuildInnerChannelListener() || context.CanBuildInnerChannelListener(); } return false; } internal override bool IsMatch(BindingElement b) { if (b == null) return false; ReliableSessionBindingElement session = b as ReliableSessionBindingElement; if (session == null) return false; if (this.acknowledgementInterval != session.acknowledgementInterval) return false; if (this.flowControlEnabled != session.flowControlEnabled) return false; if (this.inactivityTimeout != session.inactivityTimeout) return false; if (this.maxPendingChannels != session.maxPendingChannels) return false; if (this.maxRetryCount != session.maxRetryCount) return false; if (this.maxTransferWindowSize != session.maxTransferWindowSize) return false; if (this.ordered != session.ordered) return false; if (this.reliableMessagingVersion != session.reliableMessagingVersion) return false; return true; } static void ProtectProtocolMessage( ScopedMessagePartSpecification signaturePart, ScopedMessagePartSpecification encryptionPart, string action) { signaturePart.AddParts(BodyOnly, action); encryptionPart.AddParts(MessagePartSpecification.NoParts, action); //encryptionPart.AddParts(BodyOnly, action); } void SetSecuritySettings(BindingContext context) { SecurityBindingElement element = context.RemainingBindingElements.Find(); if (element != null) { element.LocalServiceSettings.ReconnectTransportOnFailure = true; } } void VerifyTransportMode(BindingContext context) { TransportBindingElement transportElement = context.RemainingBindingElements.Find(); // Verify ManualAdderssing is turned off. if ((transportElement != null) && (transportElement.ManualAddressing)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.ManualAddressingNotSupported))); } ConnectionOrientedTransportBindingElement connectionElement = transportElement as ConnectionOrientedTransportBindingElement; HttpTransportBindingElement httpElement = transportElement as HttpTransportBindingElement; // Verify TransportMode is Buffered. TransferMode transportTransferMode; if (connectionElement != null) { transportTransferMode = connectionElement.TransferMode; } else if (httpElement != null) { transportTransferMode = httpElement.TransferMode; } else { // Not one of the elements we can check, we have to assume TransferMode.Buffered. transportTransferMode = TransferMode.Buffered; } if (transportTransferMode != TransferMode.Buffered) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new InvalidOperationException(SR.GetString(SR.TransferModeNotSupported, transportTransferMode, this.GetType().Name))); } } void IPolicyExportExtension.ExportPolicy(MetadataExporter exporter, PolicyConversionContext context) { if (exporter == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); if (context == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("context"); if (context.BindingElements != null) { BindingElementCollection bindingElements = context.BindingElements; ReliableSessionBindingElement settings = bindingElements.Find(); if (settings != null) { // ReliableSession assertion XmlElement assertion = settings.CreateReliabilityAssertion(exporter.PolicyVersion, bindingElements); context.GetBindingAssertions().Add(assertion); } } } static XmlElement CreatePolicyElement(PolicyVersion policyVersion, XmlDocument doc) { string policy = MetadataStrings.WSPolicy.Elements.Policy; string policyNs = policyVersion.Namespace; string policyPrefix = MetadataStrings.WSPolicy.Prefix; return doc.CreateElement(policyPrefix, policy, policyNs); } XmlElement CreateReliabilityAssertion(PolicyVersion policyVersion, BindingElementCollection bindingElements) { XmlDocument doc = new XmlDocument(); XmlElement child = null; string reliableSessionPrefix; string reliableSessionNs; string assertionPrefix; string assertionNs; if (this.ReliableMessagingVersion == ReliableMessagingVersion.WSReliableMessagingFebruary2005) { reliableSessionPrefix = ReliableSessionPolicyStrings.ReliableSessionFebruary2005Prefix; reliableSessionNs = ReliableSessionPolicyStrings.ReliableSessionFebruary2005Namespace; assertionPrefix = reliableSessionPrefix; assertionNs = reliableSessionNs; } else { reliableSessionPrefix = ReliableSessionPolicyStrings.ReliableSession11Prefix; reliableSessionNs = ReliableSessionPolicyStrings.ReliableSession11Namespace; assertionPrefix = ReliableSessionPolicyStrings.NET11Prefix; assertionNs = ReliableSessionPolicyStrings.NET11Namespace; } // ReliableSession assertion XmlElement assertion = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.ReliableSessionName, reliableSessionNs); if (this.ReliableMessagingVersion == ReliableMessagingVersion.WSReliableMessaging11) { // Policy XmlElement policy = CreatePolicyElement(policyVersion, doc); // SequenceSTR if (IsSecureConversationEnabled(bindingElements)) { XmlElement sequenceSTR = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.SequenceSTR, reliableSessionNs); policy.AppendChild(sequenceSTR); } // DeliveryAssurance XmlElement deliveryAssurance = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.DeliveryAssurance, reliableSessionNs); // Policy XmlElement nestedPolicy = CreatePolicyElement(policyVersion, doc); // ExactlyOnce XmlElement exactlyOnce = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.ExactlyOnce, reliableSessionNs); nestedPolicy.AppendChild(exactlyOnce); if (this.ordered) { // InOrder XmlElement inOrder = doc.CreateElement(reliableSessionPrefix, ReliableSessionPolicyStrings.InOrder, reliableSessionNs); nestedPolicy.AppendChild(inOrder); } deliveryAssurance.AppendChild(nestedPolicy); policy.AppendChild(deliveryAssurance); assertion.AppendChild(policy); } // Nested InactivityTimeout assertion child = doc.CreateElement(assertionPrefix, ReliableSessionPolicyStrings.InactivityTimeout, assertionNs); WriteMillisecondsAttribute(child, this.InactivityTimeout); assertion.AppendChild(child); // Nested AcknowledgementInterval assertion child = doc.CreateElement(assertionPrefix, ReliableSessionPolicyStrings.AcknowledgementInterval, assertionNs); WriteMillisecondsAttribute(child, this.AcknowledgementInterval); assertion.AppendChild(child); return assertion; } static bool IsSecureConversationEnabled(BindingElementCollection bindingElements) { bool foundRM = false; for (int i = 0; i < bindingElements.Count; i++) { if (!foundRM) { ReliableSessionBindingElement bindingElement = bindingElements[i] as ReliableSessionBindingElement; foundRM = (bindingElement != null); } else { SecurityBindingElement securityBindingElement = bindingElements[i] as SecurityBindingElement; if (securityBindingElement != null) { SecurityBindingElement bootstrapSecurity; // The difference in bool (requireCancellation) does not affect whether the binding is valid, // but the method does match on the value so we need to pass both true and false. return SecurityBindingElement.IsSecureConversationBinding(securityBindingElement, true, out bootstrapSecurity) || SecurityBindingElement.IsSecureConversationBinding(securityBindingElement, false, out bootstrapSecurity); } break; } } return false; } static void WriteMillisecondsAttribute(XmlElement childElement, TimeSpan timeSpan) { UInt64 milliseconds = Convert.ToUInt64(timeSpan.TotalMilliseconds); childElement.SetAttribute(ReliableSessionPolicyStrings.Milliseconds, XmlConvert.ToString(milliseconds)); } class BindingDeliveryCapabilitiesHelper : IBindingDeliveryCapabilities { ReliableSessionBindingElement element; IBindingDeliveryCapabilities inner; internal BindingDeliveryCapabilitiesHelper(ReliableSessionBindingElement element, IBindingDeliveryCapabilities inner) { this.element = element; this.inner = inner; } bool IBindingDeliveryCapabilities.AssuresOrderedDelivery { get { return element.Ordered; } } bool IBindingDeliveryCapabilities.QueuedDelivery { get { return inner != null ? inner.QueuedDelivery : false; } } } } }