/// <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); }