/// <summary>
        /// Cancel a connection that is in the process of opening.
        /// </summary>
        public bool CancelConnect(CancelConnectParams cancelParams)
        {
            // Validate parameters
            if (cancelParams == null || string.IsNullOrEmpty(cancelParams.OwnerUri))
            {
                return(false);
            }

            CancelTokenKey cancelKey = new CancelTokenKey
            {
                OwnerUri = cancelParams.OwnerUri,
                Type     = cancelParams.Type
            };

            // Cancel any current connection attempts for this URI
            CancellationTokenSource source;

            if (cancelTupleToCancellationTokenSourceMap.TryGetValue(cancelKey, out source))
            {
                try
                {
                    source.Cancel();
                    return(true);
                }
                catch
                {
                    return(false);
                }
            }

            return(false);
        }
        /// <summary>
        /// Tries to create and open a connection with the given ConnectParams.
        /// </summary>
        /// <returns>null upon success, a ConnectionCompleteParams detailing the error upon failure</returns>
        private async Task <ConnectionCompleteParams> TryOpenConnection(ConnectionInfo connectionInfo, ConnectParams connectionParams)
        {
            CancellationTokenSource source     = null;
            DbConnection            connection = null;
            CancelTokenKey          cancelKey  = new CancelTokenKey {
                OwnerUri = connectionParams.OwnerUri, Type = connectionParams.Type
            };
            ConnectionCompleteParams response = new ConnectionCompleteParams {
                OwnerUri = connectionInfo.OwnerUri, Type = connectionParams.Type
            };

            try
            {
                // build the connection string from the input parameters
                string connectionString = BuildConnectionString(connectionInfo.ConnectionDetails);

                // create a sql connection instance
                connection = connectionInfo.Factory.CreateSqlConnection(connectionString);
                connectionInfo.AddConnection(connectionParams.Type, connection);

                // Add a cancellation token source so that the connection OpenAsync() can be cancelled
                using (source = new CancellationTokenSource())
                {
                    // Locking here to perform two operations as one atomic operation
                    lock (cancellationTokenSourceLock)
                    {
                        // If the URI is currently connecting from a different request, cancel it before we try to connect
                        CancellationTokenSource currentSource;
                        if (cancelTupleToCancellationTokenSourceMap.TryGetValue(cancelKey, out currentSource))
                        {
                            currentSource.Cancel();
                        }
                        cancelTupleToCancellationTokenSourceMap[cancelKey] = source;
                    }

                    // Create a task to handle cancellation requests
                    var cancellationTask = Task.Run(() =>
                    {
                        source.Token.WaitHandle.WaitOne();
                        try
                        {
                            source.Token.ThrowIfCancellationRequested();
                        }
                        catch (ObjectDisposedException)
                        {
                            // If ObjectDisposedException was thrown, then execution has already exited the
                            // "using" statment and source was disposed, meaning that the openTask completed
                            // successfully. This results in a ObjectDisposedException when trying to access
                            // source.Token and should be ignored.
                        }
                    });

                    var openTask = Task.Run(async() =>
                    {
                        await connection.OpenAsync(source.Token);
                    });

                    // Open the connection
                    await Task.WhenAny(openTask, cancellationTask).Unwrap();

                    source.Cancel();
                }
            }
            catch (SqlException ex)
            {
                response.ErrorNumber  = ex.Number;
                response.ErrorMessage = ex.Message;
                response.Messages     = ex.ToString();
                return(response);
            }
            catch (OperationCanceledException)
            {
                // OpenAsync was cancelled
                response.Messages = SR.ConnectionServiceConnectionCanceled;
                return(response);
            }
            catch (Exception ex)
            {
                response.ErrorMessage = ex.Message;
                response.Messages     = ex.ToString();
                return(response);
            }
            finally
            {
                // Remove our cancellation token from the map since we're no longer connecting
                // Using a lock here to perform two operations as one atomic operation
                lock (cancellationTokenSourceLock)
                {
                    // Only remove the token from the map if it is the same one created by this request
                    CancellationTokenSource sourceValue;
                    if (cancelTupleToCancellationTokenSourceMap.TryGetValue(cancelKey, out sourceValue) && sourceValue == source)
                    {
                        cancelTupleToCancellationTokenSourceMap.TryRemove(cancelKey, out sourceValue);
                    }
                }
            }

            // Return null upon success
            return(null);
        }
        /// <summary>
        /// Tries to create and open a connection with the given ConnectParams.
        /// </summary>
        /// <returns>null upon success, a ConnectionCompleteParams detailing the error upon failure</returns>
        private async Task <ConnectionCompleteParams> TryOpenConnection(ConnectionInfo connectionInfo, ConnectParams connectionParams)
        {
            CancellationTokenSource source     = null;
            DbConnection            connection = null;
            CancelTokenKey          cancelKey  = new CancelTokenKey {
                OwnerUri = connectionParams.OwnerUri, Type = connectionParams.Type
            };
            ConnectionCompleteParams response = new ConnectionCompleteParams {
                OwnerUri = connectionInfo.OwnerUri, Type = connectionParams.Type
            };

            try
            {
                // build the connection string from the input parameters
                string connectionString = BuildConnectionString(connectionInfo.ConnectionDetails);

                // create a sql connection instance
                connection = connectionInfo.Factory.CreateSqlConnection(connectionString);
                connectionInfo.AddConnection(connectionParams.Type, connection);

                // Add a cancellation token source so that the connection OpenAsync() can be cancelled
                source = new CancellationTokenSource();
                // Locking here to perform two operations as one atomic operation
                lock (cancellationTokenSourceLock)
                {
                    // If the URI is currently connecting from a different request, cancel it before we try to connect
                    CancellationTokenSource currentSource;
                    if (cancelTupleToCancellationTokenSourceMap.TryGetValue(cancelKey, out currentSource))
                    {
                        currentSource.Cancel();
                    }
                    cancelTupleToCancellationTokenSourceMap[cancelKey] = source;
                }

                // Open the connection
                await connection.OpenAsync(source.Token);
            }
            catch (SqlException ex)
            {
                response.ErrorNumber  = ex.Number;
                response.ErrorMessage = ex.Message;
                response.Messages     = ex.ToString();
                return(response);
            }
            catch (OperationCanceledException)
            {
                // OpenAsync was cancelled
                response.Messages = SR.ConnectionServiceConnectionCanceled;
                return(response);
            }
            catch (Exception ex)
            {
                response.ErrorMessage = ex.Message;
                response.Messages     = ex.ToString();
                return(response);
            }
            finally
            {
                // Remove our cancellation token from the map since we're no longer connecting
                // Using a lock here to perform two operations as one atomic operation
                lock (cancellationTokenSourceLock)
                {
                    // Only remove the token from the map if it is the same one created by this request
                    CancellationTokenSource sourceValue;
                    if (cancelTupleToCancellationTokenSourceMap.TryGetValue(cancelKey, out sourceValue) && sourceValue == source)
                    {
                        cancelTupleToCancellationTokenSourceMap.TryRemove(cancelKey, out sourceValue);
                    }
                    source?.Dispose();
                }
            }

            // Return null upon success
            return(null);
        }