private void DisconnectExistingConnectionIfNeeded(ConnectParams connectionParams, ConnectionInfo connectionInfo, bool disconnectAll)
        {
            // Resolve if it is an existing connection
            // Disconnect active connection if the URI is already connected for this connection type
            DbConnection existingConnection;

            if (connectionInfo.TryGetConnection(connectionParams.Type, out existingConnection))
            {
                var disconnectParams = new DisconnectParams()
                {
                    OwnerUri = connectionParams.OwnerUri,
                    Type     = disconnectAll ? null : connectionParams.Type
                };
                Disconnect(disconnectParams);
            }
        }
예제 #2
0
        /// <summary>
        /// Handle disconnect requests
        /// </summary>
        protected async Task HandleDisconnectRequest(
            DisconnectParams disconnectParams,
            RequestContext <bool> requestContext)
        {
            Logger.Write(LogLevel.Verbose, "HandleDisconnectRequest");

            try
            {
                bool result = Instance.Disconnect(disconnectParams);
                await requestContext.SendResult(result);
            }
            catch (Exception ex)
            {
                await requestContext.SendError(ex.ToString());
            }
        }
예제 #3
0
        public async void ClosingQueryConnectionShouldLeaveDefaultConnectionOpen()
        {
            // Setup the connect and disconnect params
            var connectParamsDefault = new ConnectParams()
            {
                OwnerUri   = "connectParamsSame",
                Connection = TestObjects.GetTestConnectionDetails(),
                Type       = ConnectionType.Default
            };
            var connectParamsQuery = new ConnectParams()
            {
                OwnerUri   = connectParamsDefault.OwnerUri,
                Connection = TestObjects.GetTestConnectionDetails(),
                Type       = ConnectionType.Query
            };
            var disconnectParamsQuery = new DisconnectParams()
            {
                OwnerUri = connectParamsDefault.OwnerUri,
                Type     = connectParamsQuery.Type
            };

            // If I connect a Default and a Query connection
            var service = TestObjects.GetTestConnectionService();
            await service.Connect(connectParamsDefault);

            await service.Connect(connectParamsQuery);

            ConnectionInfo connectionInfo = service.OwnerToConnectionMap[connectParamsDefault.OwnerUri];

            // There should be 2 connections in the map
            Assert.Equal(2, connectionInfo.CountConnections);

            // If I Disconnect only the Query connection, there should be 1 connection in the map
            service.Disconnect(disconnectParamsQuery);
            Assert.Equal(1, connectionInfo.CountConnections);

            // If I reconnect, there should be 2 again
            await service.Connect(connectParamsQuery);

            Assert.Equal(2, connectionInfo.CountConnections);
        }
예제 #4
0
        /// <summary>
        /// Close a connection with the specified connection details.
        /// </summary>
        public bool Disconnect(DisconnectParams disconnectParams)
        {
            // Validate parameters
            if (disconnectParams == null || string.IsNullOrEmpty(disconnectParams.OwnerUri))
            {
                return(false);
            }

            // Cancel if we are in the middle of connecting
            if (CancelConnect(new CancelConnectParams()
            {
                OwnerUri = disconnectParams.OwnerUri
            }))
            {
                return(false);
            }

            // Lookup the connection owned by the URI
            ConnectionInfo info;

            if (!ownerToConnectionMap.TryGetValue(disconnectParams.OwnerUri, out info))
            {
                return(false);
            }

            // Close the connection
            info.SqlConnection.Close();

            // Remove URI mapping
            ownerToConnectionMap.Remove(disconnectParams.OwnerUri);

            // Invoke callback notifications
            foreach (var activity in this.onDisconnectActivities)
            {
                activity(info.ConnectionDetails, disconnectParams.OwnerUri);
            }

            // Success
            return(true);
        }
예제 #5
0
        /// <summary>
        /// Close a connection with the specified connection details.
        /// </summary>
        public bool Disconnect(DisconnectParams disconnectParams)
        {
            // Validate parameters
            if (disconnectParams == null || string.IsNullOrEmpty(disconnectParams.OwnerUri))
            {
                return(false);
            }

            // Cancel if we are in the middle of connecting
            if (CancelConnect(new CancelConnectParams()
            {
                OwnerUri = disconnectParams.OwnerUri
            }))
            {
                return(false);
            }

            // Lookup the connection owned by the URI
            ConnectionInfo info;

            if (!ownerToConnectionMap.TryGetValue(disconnectParams.OwnerUri, out info))
            {
                return(false);
            }

            if (ServiceHost != null)
            {
                try
                {
                    // Send a telemetry notification for intellisense performance metrics
                    ServiceHost.SendEvent(TelemetryNotification.Type, new TelemetryParams()
                    {
                        Params = new TelemetryProperties
                        {
                            Properties = new Dictionary <string, string>
                            {
                                { "IsAzure", info.IsAzure ? "1" : "0" }
                            },
                            EventName = TelemetryEventNames.IntellisenseQuantile,
                            Measures  = info.IntellisenseMetrics.Quantile
                        }
                    });
                }
                catch (Exception ex)
                {
                    Logger.Write(LogLevel.Verbose, "Could not send Connection telemetry event " + ex.ToString());
                }
            }

            // Close the connection
            info.SqlConnection.Close();

            // Remove URI mapping
            ownerToConnectionMap.Remove(disconnectParams.OwnerUri);

            // Invoke callback notifications
            foreach (var activity in this.onDisconnectActivities)
            {
                activity(info.ConnectionDetails, disconnectParams.OwnerUri);
            }

            // Success
            return(true);
        }
예제 #6
0
        /// <summary>
        /// Open a connection with the specified connection details
        /// </summary>
        /// <param name="connectionParams"></param>
        public async Task <ConnectionCompleteParams> Connect(ConnectParams connectionParams)
        {
            // Validate parameters
            string paramValidationErrorMessage;

            if (connectionParams == null)
            {
                return(new ConnectionCompleteParams
                {
                    Messages = SR.ConnectionServiceConnectErrorNullParams
                });
            }
            if (!connectionParams.IsValid(out paramValidationErrorMessage))
            {
                return(new ConnectionCompleteParams
                {
                    OwnerUri = connectionParams.OwnerUri,
                    Messages = paramValidationErrorMessage
                });
            }

            // Resolve if it is an existing connection
            // Disconnect active connection if the URI is already connected
            ConnectionInfo connectionInfo;

            if (ownerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo))
            {
                var disconnectParams = new DisconnectParams()
                {
                    OwnerUri = connectionParams.OwnerUri
                };
                Disconnect(disconnectParams);
            }
            connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection);

            // try to connect
            var response = new ConnectionCompleteParams {
                OwnerUri = connectionParams.OwnerUri
            };
            CancellationTokenSource source = null;

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

                // create a sql connection instance
                connectionInfo.SqlConnection = connectionInfo.Factory.CreateSqlConnection(connectionString);

                // 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 (ownerToCancellationTokenSourceMap.TryGetValue(connectionParams.OwnerUri, out currentSource))
                        {
                            currentSource.Cancel();
                        }
                        ownerToCancellationTokenSourceMap[connectionParams.OwnerUri] = source;
                    }

                    // Create a task to handle cancellation requests
                    var cancellationTask = Task.Run(() =>
                    {
                        source.Token.WaitHandle.WaitOne();
                        try
                        {
                            source.Token.ThrowIfCancellationRequested();
                        }
                        catch (ObjectDisposedException)
                        {
                            // Ignore
                        }
                    });

                    var openTask = Task.Run(async() => {
                        await connectionInfo.SqlConnection.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 (ownerToCancellationTokenSourceMap.TryGetValue(connectionParams.OwnerUri, out sourceValue) && sourceValue == source)
                    {
                        ownerToCancellationTokenSourceMap.TryRemove(connectionParams.OwnerUri, out sourceValue);
                    }
                }
            }

            ownerToConnectionMap[connectionParams.OwnerUri] = connectionInfo;

            // Update with the actual database name in connectionInfo and result
            // Doing this here as we know the connection is open - expect to do this only on connecting
            connectionInfo.ConnectionDetails.DatabaseName = connectionInfo.SqlConnection.Database;
            response.ConnectionSummary = new ConnectionSummary
            {
                ServerName   = connectionInfo.ConnectionDetails.ServerName,
                DatabaseName = connectionInfo.ConnectionDetails.DatabaseName,
                UserName     = connectionInfo.ConnectionDetails.UserName,
            };

            // invoke callback notifications
            InvokeOnConnectionActivities(connectionInfo);

            // try to get information about the connected SQL Server instance
            try
            {
                var          reliableConnection = connectionInfo.SqlConnection as ReliableSqlConnection;
                DbConnection connection         = reliableConnection != null?reliableConnection.GetUnderlyingConnection() : connectionInfo.SqlConnection;

                ReliableConnectionHelper.ServerInfo serverInfo = ReliableConnectionHelper.GetServerVersion(connection);
                response.ServerInfo = new ServerInfo
                {
                    ServerMajorVersion   = serverInfo.ServerMajorVersion,
                    ServerMinorVersion   = serverInfo.ServerMinorVersion,
                    ServerReleaseVersion = serverInfo.ServerReleaseVersion,
                    EngineEditionId      = serverInfo.EngineEditionId,
                    ServerVersion        = serverInfo.ServerVersion,
                    ServerLevel          = serverInfo.ServerLevel,
                    ServerEdition        = serverInfo.ServerEdition,
                    IsCloud      = serverInfo.IsCloud,
                    AzureVersion = serverInfo.AzureVersion,
                    OsVersion    = serverInfo.OsVersion
                };
                connectionInfo.IsAzure = serverInfo.IsCloud;
            }
            catch (Exception ex)
            {
                response.Messages = ex.ToString();
            }

            // return the connection result
            response.ConnectionId = connectionInfo.ConnectionId.ToString();
            return(response);
        }
예제 #7
0
        public async void DbConnectionDoesntLeakUponDisconnect()
        {
            // If we connect with a single URI and 2 connection types
            var connectParamsDefault = new ConnectParams()
            {
                OwnerUri   = "connectParams",
                Connection = TestObjects.GetTestConnectionDetails(),
                Type       = ConnectionType.Default
            };
            var connectParamsQuery = new ConnectParams()
            {
                OwnerUri   = "connectParams",
                Connection = TestObjects.GetTestConnectionDetails(),
                Type       = ConnectionType.Query
            };
            var disconnectParams = new DisconnectParams()
            {
                OwnerUri = connectParamsDefault.OwnerUri
            };
            var service = TestObjects.GetTestConnectionService();
            await service.Connect(connectParamsDefault);

            await service.Connect(connectParamsQuery);

            // We should have one ConnectionInfo and 2 DbConnections
            ConnectionInfo connectionInfo = service.OwnerToConnectionMap[connectParamsDefault.OwnerUri];

            Assert.Equal(2, connectionInfo.CountConnections);
            Assert.Equal(1, service.OwnerToConnectionMap.Count);

            // If we record when the Default connecton calls Close()
            bool defaultDisconnectCalled = false;
            var  mockDefaultConnection   = new Mock <DbConnection> {
                CallBase = true
            };

            mockDefaultConnection.Setup(x => x.Close())
            .Callback(() =>
            {
                defaultDisconnectCalled = true;
            });
            connectionInfo.ConnectionTypeToConnectionMap[ConnectionType.Default] = mockDefaultConnection.Object;

            // And when the Query connecton calls Close()
            bool queryDisconnectCalled = false;
            var  mockQueryConnection   = new Mock <DbConnection> {
                CallBase = true
            };

            mockQueryConnection.Setup(x => x.Close())
            .Callback(() =>
            {
                queryDisconnectCalled = true;
            });
            connectionInfo.ConnectionTypeToConnectionMap[ConnectionType.Query] = mockQueryConnection.Object;

            // If we disconnect all open connections with the same URI as used above
            service.Disconnect(disconnectParams);

            // Close() should have gotten called for both DbConnections
            Assert.True(defaultDisconnectCalled);
            Assert.True(queryDisconnectCalled);

            // And the maps that hold connection data should be empty
            Assert.Equal(0, connectionInfo.CountConnections);
            Assert.Equal(0, service.OwnerToConnectionMap.Count);
        }