/// <summary>
        /// Returns the client.
        /// </summary>
        /// <param name="client">The client.</param>
        /// <param name="traceProperties">The trace properties.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task ReturnClient(IDSSmtpClient client, Dictionary <string, string> traceProperties)
        {
            if (traceProperties == null)
            {
                traceProperties = new Dictionary <string, string>();
            }

            if (client == null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            traceProperties["Id"] = Guid.NewGuid().ToString();

            await SemaphoreSlim.WaitAsync().ConfigureAwait(false);

            try
            {
                if (currentClientCount <= MaxClientCount)
                {
                    ClientList.Enqueue(client);
                    this.logger.TraceInformation("SmtpClient returned to pool.", traceProperties);
                }
                else
                {
                    currentClientCount--;
                    client.Dispose();
                    this.logger.TraceInformation($"SmtpClient disposed. Current count: {currentClientCount}", traceProperties);
                }
            }
            finally
            {
                _ = SemaphoreSlim.Release();
            }
        }
        /// <summary>
        /// Gets the client.
        /// </summary>
        /// <param name="traceProperties">The trace properties.</param>
        /// <returns>
        /// A <see cref="IDSSmtpClient" />.
        /// </returns>
        public async Task <IDSSmtpClient> GetClient(Dictionary <string, string> traceProperties)
        {
            if (traceProperties == null)
            {
                traceProperties = new Dictionary <string, string>();
            }

            traceProperties["Id"] = Guid.NewGuid().ToString();

            IDSSmtpClient client = await this.TryGetClient(traceProperties, false).ConfigureAwait(false);

            int numberOfTries = 1;

            while (client == null)
            {
                if (numberOfTries > 5)
                {
                    this.logger.WriteCustomEvent("TryGetClient exceeded max number of tries, overriding client creation.", traceProperties);
                    this.logger.WriteMetric("SmtpClientPool_OneTimeClientCreation", 1, traceProperties);
                    client = await this.TryGetClient(traceProperties, true).ConfigureAwait(false);

                    return(client);
                }
                else
                {
                    this.logger.TraceInformation($"ClientPoolRequestFailure, number of tries: {numberOfTries}.", traceProperties);
                    this.logger.WriteMetric("SmtpClientPool_ClientPoolRequestFailure", 1, traceProperties);
                    await Task.Delay(1000 *numberOfTries).ConfigureAwait(false);

                    client = await this.TryGetClient(traceProperties, false).ConfigureAwait(false);

                    numberOfTries++;
                }
            }

            return(client);
        }
        private async Task SendItemAsync(MimeMessage message, Dictionary <string, string> traceProps)
        {
            IDSSmtpClient client = null;
            string        status = "Fail";

            DateTimeOffset startTime;
            Stopwatch      timer = new Stopwatch();

            try
            {
                client = await this.clientPool.GetClient(traceProps).ConfigureAwait(false);

                startTime = DateTimeOffset.Now;
                timer.Start();
                try
                {
                    await client.SendAsync(message, traceProps).ConfigureAwait(false);
                }
                catch (ServiceNotConnectedException svcEx)
                {
                    string description = $"SmtpClient not connected: {svcEx.Source}";
                    string eventMsg    = "Closed connection, requesting new SmtpClient.";

                    this.logger.WriteException(svcEx, traceProps);
                    this.logger.TraceInformation(eventMsg, traceProps);

                    client.Refresh(traceProps);
                    await client.SendAsync(message, traceProps).ConfigureAwait(false);
                }
                catch (IOException ioex)
                {
                    string description = $"No response from MX endpoint: {ioex.Source}";
                    string eventMsg    = "Socket failure, requesting new SmtpClient.";
                    traceProps["Error Description"] = description;
                    this.logger.WriteException(ioex, traceProps);
                    this.logger.TraceInformation(eventMsg, traceProps);

                    client.Refresh(traceProps);
                    await client.SendAsync(message, traceProps).ConfigureAwait(false);
                }
                catch (SmtpCommandException ex)
                {
                    this.logger.WriteException(ex, traceProps);
                    this.logger.TraceInformation($"SmtpCommandException with message {ex.Message} has been handled ", traceProps);
                    client.Refresh(traceProps);
                    await client.SendAsync(message, traceProps).ConfigureAwait(false);
                }
                catch (SocketException ex)
                {
                    this.logger.WriteException(ex, traceProps);
                    this.logger.TraceInformation($"SocketException with message {ex.Message} has been handled ", traceProps);
                    client.Refresh(traceProps);
                    await client.SendAsync(message, traceProps).ConfigureAwait(false);
                }

                timer.Stop();
                status = "Success";
            }
            catch (SmtpProtocolException ex)
            {
                this.logger.WriteException(ex, traceProps);
                throw;
            }
            catch (SmtpCommandException ex)
            {
                string msg = string.Empty;
                switch (ex.ErrorCode)
                {
                case SmtpErrorCode.RecipientNotAccepted:
                    msg = $"Recipient not accepted: {ex.Mailbox?.Address}";
                    break;

                case SmtpErrorCode.SenderNotAccepted:
                    msg = $"Sender not accepted: {ex.Mailbox?.Address}";
                    break;

                case SmtpErrorCode.MessageNotAccepted:
                    msg = "Message not accepted.";
                    break;
                }

                traceProps["ErrorMessage"] = msg;
                this.logger.WriteException(ex, traceProps);
                throw;
            }
            catch (Exception ex)
            {
                this.logger.WriteException(ex, traceProps);
                throw;
            }
            finally
            {
                if (client != null)
                {
                    await this.clientPool.ReturnClient(client, traceProps).ConfigureAwait(false);
                }

                traceProps["Status"]   = status;
                traceProps["EndPoint"] = this.clientPool.EndPoint;
                var metrics = new Dictionary <string, double>
                {
                    { "Duration", timer.Elapsed.TotalMilliseconds },
                };
                this.logger.WriteCustomEvent("DirectSendMailService_SendMail", traceProps, metrics);

                this.logger.WriteMetric("DirectSendMailService_SendMailCount", 1, traceProps);
            }
        }
        /// <summary>
        /// Creates the client.
        /// </summary>
        /// <param name="traceProperties">The trace properties.</param>
        /// <returns>A <see cref="IDSSmtpClient"/>.</returns>
        internal IDSSmtpClient CreateClient()
        {
            IDSSmtpClient emailClient = this.factory.CreateClient(this.config, this.logger);

            return(emailClient);
        }