File: System\ServiceModel\Dispatcher\ChannelDispatcher.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel.Dispatcher
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Diagnostics.Application;
    using System.Text;
    using System.Transactions;
 
    public class ChannelDispatcher : ChannelDispatcherBase
    {
        ThreadSafeMessageFilterTable<EndpointAddress> addressTable;
        string bindingName;
        SynchronizedCollection<IChannelInitializer> channelInitializers;
        CommunicationObjectManager<IChannel> channels;
        EndpointDispatcherCollection endpointDispatchers;
        Collection<IErrorHandler> errorHandlers;
        EndpointDispatcherTable filterTable;
        ServiceHostBase host;
        bool isTransactedReceive;
        bool asynchronousTransactedAcceptEnabled;
        bool receiveContextEnabled;
        readonly IChannelListener listener;
        ListenerHandler listenerHandler;
        int maxTransactedBatchSize;
        MessageVersion messageVersion;
        SynchronizedChannelCollection<IChannel> pendingChannels; // app has not yet seen these.
        bool receiveSynchronously;
        bool sendAsynchronously;
        int maxPendingReceives;
        bool includeExceptionDetailInFaults;
        ServiceThrottle serviceThrottle;
        bool session;
        SharedRuntimeState shared;
        IDefaultCommunicationTimeouts timeouts;
        IsolationLevel transactionIsolationLevel = ServiceBehaviorAttribute.DefaultIsolationLevel;
        bool transactionIsolationLevelSet;
        TimeSpan transactionTimeout;
        bool performDefaultCloseInput;
        EventTraceActivity eventTraceActivity;
        ErrorBehavior errorBehavior;
 
        internal ChannelDispatcher(SharedRuntimeState shared)
        {
            this.Initialize(shared);
        }
 
        public ChannelDispatcher(IChannelListener listener)
            : this(listener, null, null)
        {
        }
 
        public ChannelDispatcher(IChannelListener listener, string bindingName)
            : this(listener, bindingName, null)
        {
        }
 
        public ChannelDispatcher(IChannelListener listener, string bindingName, IDefaultCommunicationTimeouts timeouts)
        {
            if (listener == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("listener");
            }
 
            this.listener = listener;
            this.bindingName = bindingName;
            this.timeouts = new ImmutableCommunicationTimeouts(timeouts);
 
            this.session = ((listener is IChannelListener<IInputSessionChannel>) ||
                            (listener is IChannelListener<IReplySessionChannel>) ||
                            (listener is IChannelListener<IDuplexSessionChannel>));
 
            this.Initialize(new SharedRuntimeState(true));
        }
 
        void Initialize(SharedRuntimeState shared)
        {
            this.shared = shared;
            this.endpointDispatchers = new EndpointDispatcherCollection(this);
            this.channelInitializers = this.NewBehaviorCollection<IChannelInitializer>();
            this.channels = new CommunicationObjectManager<IChannel>(this.ThisLock);
            this.pendingChannels = new SynchronizedChannelCollection<IChannel>(this.ThisLock);
            this.errorHandlers = new Collection<IErrorHandler>();
            this.isTransactedReceive = false;
            this.asynchronousTransactedAcceptEnabled = false;
            this.receiveSynchronously = false;
            this.serviceThrottle = null;
            this.transactionTimeout = TimeSpan.Zero;
            this.maxPendingReceives = MultipleReceiveBinder.MultipleReceiveDefaults.MaxPendingReceives;
            if (this.listener != null)
            {
                this.listener.Faulted += new EventHandler(OnListenerFaulted);
            }
        }
 
        public string BindingName
        {
            get { return this.bindingName; }
        }
 
        public SynchronizedCollection<IChannelInitializer> ChannelInitializers
        {
            get { return this.channelInitializers; }
        }
 
        protected override TimeSpan DefaultCloseTimeout
        {
            get
            {
                if (this.timeouts != null)
                {
                    return this.timeouts.CloseTimeout;
                }
                else
                {
                    return ServiceDefaults.CloseTimeout;
                }
            }
        }
 
        protected override TimeSpan DefaultOpenTimeout
        {
            get
            {
                if (this.timeouts != null)
                {
                    return this.timeouts.OpenTimeout;
                }
                else
                {
                    return ServiceDefaults.OpenTimeout;
                }
            }
        }
 
        internal EndpointDispatcherTable EndpointDispatcherTable
        {
            get { return this.filterTable; }
        }
 
        internal CommunicationObjectManager<IChannel> Channels
        {
            get { return this.channels; }
        }
 
        public SynchronizedCollection<EndpointDispatcher> Endpoints
        {
            get { return this.endpointDispatchers; }
        }
 
        public Collection<IErrorHandler> ErrorHandlers
        {
            get { return this.errorHandlers; }
        }
 
        public MessageVersion MessageVersion
        {
            get { return this.messageVersion; }
            set
            {
                this.messageVersion = value;
                this.ThrowIfDisposedOrImmutable();
            }
        }
 
        internal bool IsServiceThrottleReplaced { get; set; } = false;
 
        internal bool Session
        {
            get { return this.session; }
        }
 
        public override ServiceHostBase Host
        {
            get { return this.host; }
        }
 
        internal bool EnableFaults
        {
            get { return this.shared.EnableFaults; }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.shared.EnableFaults = value;
            }
        }
 
        internal bool IsOnServer
        {
            get { return this.shared.IsOnServer; }
        }
 
        public bool IsTransactedAccept
        {
            get { return this.isTransactedReceive && this.session; }
        }
 
        public bool IsTransactedReceive
        {
            get
            {
                return this.isTransactedReceive;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.isTransactedReceive = value;
            }
        }
 
        public bool AsynchronousTransactedAcceptEnabled
        {
            get
            {
                return this.asynchronousTransactedAcceptEnabled;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.asynchronousTransactedAcceptEnabled = value;
            }
        }
 
        public bool ReceiveContextEnabled
        {
            get
            {
                return this.receiveContextEnabled;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.receiveContextEnabled = value;
            }
        }
 
        internal bool BufferedReceiveEnabled
        {
            get;
            set;
        }
 
        public override IChannelListener Listener
        {
            get { return this.listener; }
        }
 
        public int MaxTransactedBatchSize
        {
            get
            {
                return this.maxTransactedBatchSize;
            }
            set
            {
                if (value < 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                                                    SR.GetString(SR.ValueMustBeNonNegative)));
                }
 
                this.ThrowIfDisposedOrImmutable();
                this.maxTransactedBatchSize = value;
            }
        }
 
        public ServiceThrottle ServiceThrottle
        {
            get
            {
                return this.serviceThrottle;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.serviceThrottle = value;
            }
        }
 
        public bool ManualAddressing
        {
            get { return this.shared.ManualAddressing; }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.shared.ManualAddressing = value;
            }
        }
 
        internal SynchronizedChannelCollection<IChannel> PendingChannels
        {
            get { return this.pendingChannels; }
        }
 
        public bool ReceiveSynchronously
        {
            get
            {
                return this.receiveSynchronously;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.receiveSynchronously = value;
            }
        }
 
        public bool SendAsynchronously
        {
            get
            {
                return this.sendAsynchronously;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.sendAsynchronously = value;
            }
 
        }
 
        public int MaxPendingReceives
        {
            get
            {
                return this.maxPendingReceives;
            }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.maxPendingReceives = value;
            }
        }
 
        public bool IncludeExceptionDetailInFaults
        {
            get { return this.includeExceptionDetailInFaults; }
            set
            {
                lock (this.ThisLock)
                {
                    this.ThrowIfDisposedOrImmutable();
                    this.includeExceptionDetailInFaults = value;
                }
            }
        }
 
        internal IDefaultCommunicationTimeouts DefaultCommunicationTimeouts
        {
            get { return this.timeouts; }
        }
 
        public IsolationLevel TransactionIsolationLevel
        {
            get { return this.transactionIsolationLevel; }
            set
            {
                switch (value)
                {
                    case IsolationLevel.Serializable:
                    case IsolationLevel.RepeatableRead:
                    case IsolationLevel.ReadCommitted:
                    case IsolationLevel.ReadUncommitted:
                    case IsolationLevel.Unspecified:
                    case IsolationLevel.Chaos:
                    case IsolationLevel.Snapshot:
                        break;
 
                    default:
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
                }
 
                this.ThrowIfDisposedOrImmutable();
                this.transactionIsolationLevel = value;
                this.transactionIsolationLevelSet = true;
            }
        }
 
        internal bool TransactionIsolationLevelSet
        {
            get { return this.transactionIsolationLevelSet; }
        }
 
        public TimeSpan TransactionTimeout
        {
            get
            {
                return this.transactionTimeout;
            }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SR.GetString(SR.SFxTimeoutOutOfRange0)));
                }
 
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value,
                        SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
                }
 
                this.ThrowIfDisposedOrImmutable();
                this.transactionTimeout = value;
            }
        }
 
        void AbortPendingChannels()
        {
            lock (this.ThisLock)
            {
                for (int i = this.pendingChannels.Count - 1; i >= 0; i--)
                {
                    this.pendingChannels[i].Abort();
                }
            }
        }
 
        internal override void CloseInput(TimeSpan timeout)
        {
            // we have to perform some slightly convoluted logic here due to 
            // backwards compat. We probably need an IAsyncChannelDispatcher 
            // interface that has timeouts and async
            this.CloseInput();
 
            if (this.performDefaultCloseInput)
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                lock (this.ThisLock)
                {
                    if (DiagnosticUtility.ShouldTraceInformation)
                    {
                        for (int i = 0; i < this.endpointDispatchers.Count; i++)
                        {
                            EndpointDispatcher endpointDispatcher = this.endpointDispatchers[i];
                            this.TraceEndpointLifetime(endpointDispatcher, TraceCode.EndpointListenerClose, SR.GetString(SR.TraceCodeEndpointListenerClose));
                        }
                    }
 
                    ListenerHandler handler = this.listenerHandler;
                    if (handler != null)
                    {
                        handler.CloseInput(timeoutHelper.RemainingTime());
                    }
                }
 
                if (!this.session)
                {
                    ListenerHandler handler = this.listenerHandler;
                    if (handler != null)
                    {
                        handler.Close(timeoutHelper.RemainingTime());
                    }
                }
            }
        }
 
        internal void ReleasePerformanceCounters()
        {
            if (PerformanceCounters.PerformanceCountersEnabled)
            {
                for (int i = 0; i < this.endpointDispatchers.Count; i++)
                {
                    if (null != this.endpointDispatchers[i])
                    {
                        this.endpointDispatchers[i].ReleasePerformanceCounters();
                    }
                }
            }
        }
 
        public override void CloseInput()
        {
            this.performDefaultCloseInput = true;
        }
 
        void OnListenerFaulted(object sender, EventArgs e)
        {
            this.Fault();
        }
 
        internal bool HandleError(Exception error)
        {
            ErrorHandlerFaultInfo dummy = new ErrorHandlerFaultInfo();
            return this.HandleError(error, ref dummy);
        }
 
        internal bool HandleError(Exception error, ref ErrorHandlerFaultInfo faultInfo)
        {
            ErrorBehavior behavior;
 
            lock (this.ThisLock)
            {
                if (this.errorBehavior != null)
                {
                    behavior = this.errorBehavior;
                }
                else
                {
                    behavior = new ErrorBehavior(this);
                }
            }
 
            if (behavior != null)
            {
                return behavior.HandleError(error, ref faultInfo);
            }
            else
            {
                return false;
            }
        }
 
        internal void InitializeChannel(IClientChannel channel)
        {
            this.ThrowIfDisposedOrNotOpen();
            try
            {
                for (int i = 0; i < this.channelInitializers.Count; ++i)
                {
                    this.channelInitializers[i].Initialize(channel);
                }
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(e);
            }
        }
 
        internal EndpointDispatcher Match(Message message, out bool addressMatched)
        {
            lock (this.ThisLock)
            {
                return this.filterTable.Lookup(message, out addressMatched);
            }
        }
 
        internal SynchronizedCollection<T> NewBehaviorCollection<T>()
        {
            return new ChannelDispatcherBehaviorCollection<T>(this);
        }
 
        internal bool HasApplicationEndpoints()
        {
            foreach (EndpointDispatcher endpointDispatcher in this.Endpoints)
            {
                if (!endpointDispatcher.IsSystemEndpoint)
                {
                    return true;
                }
            }
            return false;
        }
 
        void OnAddEndpoint(EndpointDispatcher endpoint)
        {
            lock (this.ThisLock)
            {
                endpoint.Attach(this);
 
                if (this.State == CommunicationState.Opened)
                {
                    if (this.addressTable != null)
                    {
                        this.addressTable.Add(endpoint.AddressFilter, endpoint.EndpointAddress, endpoint.FilterPriority);
                    }
 
                    this.filterTable.AddEndpoint(endpoint);
                }
            }
        }
 
        void OnRemoveEndpoint(EndpointDispatcher endpoint)
        {
            lock (this.ThisLock)
            {
                if (this.State == CommunicationState.Opened)
                {
                    this.filterTable.RemoveEndpoint(endpoint);
 
                    if (this.addressTable != null)
                    {
                        this.addressTable.Remove(endpoint.AddressFilter);
                    }
                }
 
                endpoint.Detach(this);
            }
        }
 
        protected override void OnAbort()
        {
            if (this.listener != null)
            {
                this.listener.Abort();
            }
 
            ListenerHandler handler = this.listenerHandler;
            if (handler != null)
            {
                handler.Abort();
            }
 
            this.AbortPendingChannels();
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            if (this.listener != null)
            {
                this.listener.Close(timeoutHelper.RemainingTime());
            }
 
            ListenerHandler handler = this.listenerHandler;
            if (handler != null)
            {
                handler.Close(timeoutHelper.RemainingTime());
            }
 
            this.AbortPendingChannels();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            List<ICommunicationObject> list = new List<ICommunicationObject>();
 
            if (this.listener != null)
            {
                list.Add(this.listener);
            }
 
            ListenerHandler handler = this.listenerHandler;
            if (handler != null)
            {
                list.Add(handler);
            }
 
            return new CloseCollectionAsyncResult(timeout, callback, state, list);
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            try
            {
                CloseCollectionAsyncResult.End(result);
            }
            finally
            {
                this.AbortPendingChannels();
            }
        }
 
        protected override void OnClosed()
        {
            base.OnClosed();
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                for (int i = 0; i < this.endpointDispatchers.Count; i++)
                {
                    EndpointDispatcher endpointDispatcher = this.endpointDispatchers[i];
                    this.TraceEndpointLifetime(endpointDispatcher, TraceCode.EndpointListenerClose, SR.GetString(SR.TraceCodeEndpointListenerClose));
                }
            }
        }
 
        protected override void OnOpen(TimeSpan timeout)
        {
            ThrowIfNotAttachedToHost();
            ThrowIfNoMessageVersion();
 
            if (this.listener != null)
            {
                try
                {
                    this.listener.Open(timeout);
                }
                catch (InvalidOperationException e)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateOuterExceptionWithEndpointsInformation(e));
                }
            }
        }
 
        InvalidOperationException CreateOuterExceptionWithEndpointsInformation(InvalidOperationException e)
        {
            string endpointContractNames = CreateContractListString();
 
            if (String.IsNullOrEmpty(endpointContractNames))
            {
                return new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherUnableToOpen1, this.listener.Uri), e);
            }
            else
            {
                return new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherUnableToOpen2, this.listener.Uri, endpointContractNames), e);
            }
 
        }
 
        internal string CreateContractListString()
        {
            const string OpenQuote = "\"";
            const string CloseQuote = "\"";
            const string Space = " ";
 
            Collection<string> namesSeen = new Collection<string>();
            StringBuilder endpointContractNames = new StringBuilder();
 
            lock (this.ThisLock)
            {
                foreach (EndpointDispatcher ed in this.Endpoints)
                {
                    if (!namesSeen.Contains(ed.ContractName))
                    {
                        if (endpointContractNames.Length > 0)
                        {
                            endpointContractNames.Append(CultureInfo.CurrentCulture.TextInfo.ListSeparator);
                            endpointContractNames.Append(Space);
                        }
 
                        endpointContractNames.Append(OpenQuote);
                        endpointContractNames.Append(ed.ContractName);
                        endpointContractNames.Append(CloseQuote);
 
                        namesSeen.Add(ed.ContractName);
                    }
                }
            }
 
            return endpointContractNames.ToString();
        }
 
        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            ThrowIfNotAttachedToHost();
            ThrowIfNoMessageVersion();
 
            if (this.listener != null)
            {
                try
                {
                    return this.listener.BeginOpen(timeout, callback, state);
                }
                catch (InvalidOperationException e)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateOuterExceptionWithEndpointsInformation(e));
                }
            }
            else
            {
                return new CompletedAsyncResult(callback, state);
            }
        }
 
        protected override void OnEndOpen(IAsyncResult result)
        {
            if (this.listener != null)
            {
                try
                {
                    this.listener.EndOpen(result);
                }
                catch (InvalidOperationException e)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateOuterExceptionWithEndpointsInformation(e));
                }
            }
            else
            {
                CompletedAsyncResult.End(result);
            }
        }
 
        protected override void OnOpening()
        {
            ThrowIfNotAttachedToHost();
 
            if (TD.ListenerOpenStartIsEnabled())
            {
                this.eventTraceActivity = EventTraceActivity.GetFromThreadOrCreate();
                TD.ListenerOpenStart(this.eventTraceActivity,
                    (this.Listener != null) ? this.Listener.Uri.ToString() : string.Empty,
                    (this.host != null && host.EventTraceActivity != null) ? this.host.EventTraceActivity.ActivityId : Guid.Empty);
            }
 
            base.OnOpening();
        }
 
        protected override void OnOpened()
        {
            ThrowIfNotAttachedToHost();
            base.OnOpened();
 
            if (TD.ListenerOpenStopIsEnabled())
            {
                TD.ListenerOpenStop(this.eventTraceActivity);
                this.eventTraceActivity = null; // clear this since we don't need this anymore.
            }
 
            this.errorBehavior = new ErrorBehavior(this);
 
            this.filterTable = new EndpointDispatcherTable(this.ThisLock);
            for (int i = 0; i < this.endpointDispatchers.Count; i++)
            {
                EndpointDispatcher endpoint = this.endpointDispatchers[i];
 
                // Force a build of the runtime to catch any unexpected errors before we are done opening.
                endpoint.DispatchRuntime.GetRuntime();
                // Lock down the DispatchRuntime.
                endpoint.DispatchRuntime.LockDownProperties();
 
                this.filterTable.AddEndpoint(endpoint);
 
                if ((this.addressTable != null) && (endpoint.OriginalAddress != null))
                {
                    this.addressTable.Add(endpoint.AddressFilter, endpoint.OriginalAddress, endpoint.FilterPriority);
                }
 
                if (DiagnosticUtility.ShouldTraceInformation)
                {
                    this.TraceEndpointLifetime(endpoint, TraceCode.EndpointListenerOpen, SR.GetString(SR.TraceCodeEndpointListenerOpen));
                }
            }
 
            ServiceThrottle throttle = this.serviceThrottle;
            if (throttle == null)
            {
                throttle = this.host.ServiceThrottle;
            }
 
            IListenerBinder binder = ListenerBinder.GetBinder(this.listener, this.messageVersion);
            this.listenerHandler = new ListenerHandler(binder, this, this.host, throttle, this.timeouts);
            this.listenerHandler.Open();  // This never throws, which is why it's ok for it to happen in OnOpened
        }
 
        internal void ProvideFault(Exception e, FaultConverter faultConverter, ref ErrorHandlerFaultInfo faultInfo)
        {
            ErrorBehavior behavior;
 
            lock (this.ThisLock)
            {
                if (this.errorBehavior != null)
                {
                    behavior = this.errorBehavior;
                }
                else
                {
                    behavior = new ErrorBehavior(this);
                }
            }
 
            behavior.ProvideFault(e, faultConverter, ref faultInfo);
        }
 
        internal void SetEndpointAddressTable(ThreadSafeMessageFilterTable<EndpointAddress> table)
        {
            if (table == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("table");
            }
 
            this.ThrowIfDisposedOrImmutable();
 
            this.addressTable = table;
        }
 
        internal new void ThrowIfDisposedOrImmutable()
        {
            base.ThrowIfDisposedOrImmutable();
            this.shared.ThrowIfImmutable();
        }
 
        void ThrowIfNotAttachedToHost()
        {
            // if we are on the server, we need a host
            // if we are on the client, we never call Open(), so this method is not invoked
            if (this.host == null)
            {
                Exception error = new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherNoHost0));
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        void ThrowIfNoMessageVersion()
        {
            if (this.messageVersion == null)
            {
                Exception error = new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherNoMessageVersion));
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        void TraceEndpointLifetime(EndpointDispatcher endpoint, int traceCode, string traceDescription)
        {
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                Dictionary<string, object> values = new Dictionary<string, object>(3)
                {
                    { "ContractNamespace",  endpoint.ContractNamespace },
                    { "ContractName",  endpoint.ContractName },
                    { "Endpoint",  endpoint.ListenUri }
                };
                TraceUtility.TraceEvent(TraceEventType.Information, traceCode,
                    traceDescription, new DictionaryTraceRecord(values), endpoint, null);
            }
        }
 
        protected override void Attach(ServiceHostBase host)
        {
            if (host == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("host");
            }
 
            ServiceHostBase serviceHost = host;
 
            this.ThrowIfDisposedOrImmutable();
 
            if (this.host != null)
            {
                Exception error = new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherMultipleHost0));
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
 
            this.host = serviceHost;
        }
 
        protected override void Detach(ServiceHostBase host)
        {
            if (host == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("host");
            }
 
            if (this.host != host)
            {
                Exception error = new InvalidOperationException(SR.GetString(SR.SFxChannelDispatcherDifferentHost0));
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
 
            this.ThrowIfDisposedOrImmutable();
 
            this.host = null;
        }
 
        class EndpointDispatcherCollection : SynchronizedCollection<EndpointDispatcher>
        {
            ChannelDispatcher owner;
 
            internal EndpointDispatcherCollection(ChannelDispatcher owner)
                : base(owner.ThisLock)
            {
                this.owner = owner;
            }
 
            protected override void ClearItems()
            {
                foreach (EndpointDispatcher item in this.Items)
                {
                    this.owner.OnRemoveEndpoint(item);
                }
                base.ClearItems();
            }
 
            protected override void InsertItem(int index, EndpointDispatcher item)
            {
                if (item == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
 
                this.owner.OnAddEndpoint(item);
                base.InsertItem(index, item);
            }
 
            protected override void RemoveItem(int index)
            {
                EndpointDispatcher item = this.Items[index];
                base.RemoveItem(index);
                this.owner.OnRemoveEndpoint(item);
            }
 
            protected override void SetItem(int index, EndpointDispatcher item)
            {
                Exception error = new InvalidOperationException(SR.GetString(SR.SFxCollectionDoesNotSupportSet0));
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        class ChannelDispatcherBehaviorCollection<T> : SynchronizedCollection<T>
        {
            ChannelDispatcher outer;
 
            internal ChannelDispatcherBehaviorCollection(ChannelDispatcher outer)
                : base(outer.ThisLock)
            {
                this.outer = outer;
            }
 
            protected override void ClearItems()
            {
                this.outer.ThrowIfDisposedOrImmutable();
                base.ClearItems();
            }
 
            protected override void InsertItem(int index, T item)
            {
                if (item == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
                }
 
                this.outer.ThrowIfDisposedOrImmutable();
                base.InsertItem(index, item);
            }
 
            protected override void RemoveItem(int index)
            {
                this.outer.ThrowIfDisposedOrImmutable();
                base.RemoveItem(index);
            }
 
            protected override void SetItem(int index, T item)
            {
                if (item == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("item");
                }
 
                this.outer.ThrowIfDisposedOrImmutable();
                base.SetItem(index, item);
            }
        }
    }
}