File: System\ServiceModel\Channels\ChannelFactoryBase.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
 
using System.Runtime;
using System.Threading.Tasks;
 
namespace System.ServiceModel.Channels
{
    public abstract class ChannelFactoryBase : ChannelManagerBase, IChannelFactory, IAsyncChannelFactory
    {
        private TimeSpan _closeTimeout = ServiceDefaults.CloseTimeout;
        private TimeSpan _openTimeout = ServiceDefaults.OpenTimeout;
        private TimeSpan _receiveTimeout = ServiceDefaults.ReceiveTimeout;
        private TimeSpan _sendTimeout = ServiceDefaults.SendTimeout;
 
        protected ChannelFactoryBase()
        {
        }
 
        protected ChannelFactoryBase(IDefaultCommunicationTimeouts timeouts)
        {
            InitializeTimeouts(timeouts);
        }
 
        protected override TimeSpan DefaultCloseTimeout
        {
            get { return _closeTimeout; }
        }
 
        protected override TimeSpan DefaultOpenTimeout
        {
            get { return _openTimeout; }
        }
 
        protected override TimeSpan DefaultReceiveTimeout
        {
            get { return _receiveTimeout; }
        }
 
        protected override TimeSpan DefaultSendTimeout
        {
            get { return _sendTimeout; }
        }
 
        public virtual T GetProperty<T>()
            where T : class
        {
            if (typeof(T) == typeof(IChannelFactory))
            {
                return (T)(object)this;
            }
 
            return default(T);
        }
 
        protected override void OnAbort()
        {
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new CompletedAsyncResult(callback, state);
        }
 
        protected internal override Task OnCloseAsync(TimeSpan timeout)
        {
            return TaskHelpers.CompletedTask();
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            CompletedAsyncResult.End(result);
        }
 
        private void InitializeTimeouts(IDefaultCommunicationTimeouts timeouts)
        {
            if (timeouts != null)
            {
                _closeTimeout = timeouts.CloseTimeout;
                _openTimeout = timeouts.OpenTimeout;
                _receiveTimeout = timeouts.ReceiveTimeout;
                _sendTimeout = timeouts.SendTimeout;
            }
        }
    }
 
    public abstract class ChannelFactoryBase<TChannel> : ChannelFactoryBase, IChannelFactory<TChannel>
    {
        private CommunicationObjectManager<IChannel> _channels;
 
        protected ChannelFactoryBase()
            : this(null)
        {
        }
 
        protected ChannelFactoryBase(IDefaultCommunicationTimeouts timeouts)
            : base(timeouts)
        {
            _channels = new CommunicationObjectManager<IChannel>(ThisLock);
        }
 
        public TChannel CreateChannel(EndpointAddress address)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(address));
            }
 
            return InternalCreateChannel(address, address.Uri);
        }
 
        public TChannel CreateChannel(EndpointAddress address, Uri via)
        {
            if (address == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(address));
            }
 
            if (via == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(via));
            }
 
            return InternalCreateChannel(address, via);
        }
 
        private TChannel InternalCreateChannel(EndpointAddress address, Uri via)
        {
            ValidateCreateChannel();
            TChannel channel = OnCreateChannel(address, via);
 
            bool success = false;
 
            try
            {
                _channels.Add((IChannel)(object)channel);
                success = true;
            }
            finally
            {
                if (!success)
                {
                    ((IChannel)(object)channel).Abort();
                }
            }
 
            return channel;
        }
 
        protected abstract TChannel OnCreateChannel(EndpointAddress address, Uri via);
 
        protected void ValidateCreateChannel()
        {
            ThrowIfDisposed();
            if (State != CommunicationState.Opened)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.ChannelFactoryCannotBeUsedToCreateChannels, GetType().ToString())));
            }
        }
 
        protected override void OnAbort()
        {
            IChannel[] currentChannels = _channels.ToArray();
            foreach (IChannel channel in currentChannels)
            {
                channel.Abort();
            }
 
            _channels.Abort();
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            IChannel[] currentChannels = _channels.ToArray();
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            foreach (IChannel channel in currentChannels)
            {
                channel.Close(timeoutHelper.RemainingTime());
            }
 
            _channels.Close(timeoutHelper.RemainingTime());
        }
 
        protected internal override Task OnCloseAsync(TimeSpan timeout)
        {
            return OnCloseAsyncInternal(timeout);
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return OnCloseAsync(timeout).ToApm(callback, state);
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            result.ToApmEnd();
        }
 
        private async Task OnCloseAsyncInternal(TimeSpan timeout)
        {
            IChannel[] currentChannels = _channels.ToArray();
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            foreach (IChannel channel in currentChannels)
            {
                await CloseOtherAsync(channel, timeoutHelper.RemainingTime());
            }
 
            // CommunicationObjectManager (_channels) is not a CommunicationObject,
            // and it's close method waits for it to not be busy. Calling CloseAsync
            // is the correct option here as there's no need to block this thread
            // waiting on a signal from another thread to notify it's no longer busy.
            await _channels.CloseAsync(timeoutHelper.RemainingTime());
        }
    }
}