private void RunConnectRequestHandlerTask(ConnectParams connectParams) { // create a task to connect asynchronously so that other requests are not blocked in the meantime Task.Run(async() => { try { // result is null if the ConnectParams was successfully validated ConnectionCompleteParams result = ValidateConnectParams(connectParams); if (result != null) { await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result); return; } // open connection based on request details result = await Connect(connectParams); await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result); } catch (Exception ex) { ConnectionCompleteParams result = new ConnectionCompleteParams() { Messages = ex.ToString() }; await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result); } }); }
private async Task <ConnectionCompleteParams> Connect(ConnectParams connectParams, string uri) { string connectionErrorMessage = string.Empty; try { // open connection based on request details ConnectionCompleteParams result = await connectionService.Connect(connectParams); connectionErrorMessage = result != null ? $"{result.Messages} error code:{result.ErrorNumber}" : string.Empty; if (result != null && !string.IsNullOrEmpty(result.ConnectionId)) { return(result); } else { await SendSessionFailedNotification(uri, result.ErrorMessage); return(null); } } catch (Exception ex) { await SendSessionFailedNotification(uri, ex.ToString()); return(null); } }
/// <summary> /// Establishes a new session and stores its information /// </summary> /// <returns><see cref="ObjectExplorerSession"/> object if successful, null if unsuccessful</returns> internal async Task <ObjectExplorerSession> DoCreateSession(ConnectionDetails connectionDetails, string uri) { try { ObjectExplorerSession session; connectionDetails.PersistSecurityInfo = true; ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails }; ConnectionCompleteParams connectionResult = await Connect(connectParams, uri); if (connectionResult == null) { // Connection failed and notification is already sent return(null); } session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider); sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session); return(session); } catch (Exception ex) { await SendSessionFailedNotification(uri, ex.Message); return(null); } }
/// <summary> /// Open a connection with the specified ConnectParams /// </summary> public virtual async Task <ConnectionCompleteParams> Connect(ConnectParams connectionParams) { // Validate parameters ConnectionCompleteParams validationResults = ValidateConnectParams(connectionParams); if (validationResults != null) { return(validationResults); } // If there is no ConnectionInfo in the map, create a new ConnectionInfo, // but wait until later when we are connected to add it to the map. ConnectionInfo connectionInfo; bool connectionChanged = false; if (!ownerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo)) { connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection); } else if (IsConnectionChanged(connectionParams, connectionInfo)) { // We are actively changing the connection information for this connection. We must disconnect // all active connections, since it represents a full context change connectionChanged = true; } DisconnectExistingConnectionIfNeeded(connectionParams, connectionInfo, disconnectAll: connectionChanged); if (connectionChanged) { connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection); } // Try to open a connection with the given ConnectParams ConnectionCompleteParams response = await TryOpenConnection(connectionInfo, connectionParams); if (response != null) { return(response); } // If this is the first connection for this URI, add the ConnectionInfo to the map bool addToMap = connectionChanged || !ownerToConnectionMap.ContainsKey(connectionParams.OwnerUri); if (addToMap) { ownerToConnectionMap[connectionParams.OwnerUri] = connectionInfo; } // Return information about the connected SQL Server instance ConnectionCompleteParams completeParams = GetConnectionCompleteParams(connectionParams.Type, connectionInfo); // Invoke callback notifications InvokeOnConnectionActivities(connectionInfo, connectionParams); return(completeParams); }
/// <summary> /// Establishes a new session and stores its information /// </summary> /// <returns><see cref="ObjectExplorerSession"/> object if successful, null if unsuccessful</returns> internal async Task <ObjectExplorerSession> DoCreateSession(ConnectionDetails connectionDetails, string uri) { try { ObjectExplorerSession session = null; connectionDetails.PersistSecurityInfo = true; ConnectParams connectParams = new ConnectParams() { OwnerUri = uri, Connection = connectionDetails, Type = Connection.ConnectionType.ObjectExplorer }; bool isDefaultOrSystemDatabase = DatabaseUtils.IsSystemDatabaseConnection(connectionDetails.DatabaseName) || string.IsNullOrWhiteSpace(connectionDetails.DatabaseDisplayName); ConnectionInfo connectionInfo; ConnectionCompleteParams connectionResult = await Connect(connectParams, uri); if (!connectionService.TryFindConnection(uri, out connectionInfo)) { return(null); } if (connectionResult == null) { // Connection failed and notification is already sent return(null); } int timeout = (int)TimeSpan.FromSeconds(settings?.CreateSessionTimeout ?? ObjectExplorerSettings.DefaultCreateSessionTimeout).TotalMilliseconds; QueueItem queueItem = bindingQueue.QueueBindingOperation( key: bindingQueue.AddConnectionContext(connectionInfo, connectionName), bindingTimeout: timeout, waitForLockTimeout: timeout, bindOperation: (bindingContext, cancelToken) => { session = ObjectExplorerSession.CreateSession(connectionResult, serviceProvider, bindingContext.ServerConnection, isDefaultOrSystemDatabase); session.ConnectionInfo = connectionInfo; sessionMap.AddOrUpdate(uri, session, (key, oldSession) => session); return(session); }); queueItem.ItemProcessed.WaitOne(); if (queueItem.GetResultAsT <ObjectExplorerSession>() != null) { session = queueItem.GetResultAsT <ObjectExplorerSession>(); } return(session); } catch (Exception ex) { await SendSessionFailedNotification(uri, ex.Message); return(null); } }
/// <summary> /// Open a connection with the specified ConnectParams /// </summary> public async Task <ConnectionCompleteParams> Connect(ConnectParams connectionParams) { // Validate parameters ConnectionCompleteParams validationResults = ValidateConnectParams(connectionParams); if (validationResults != null) { return(validationResults); } // If there is no ConnectionInfo in the map, create a new ConnectionInfo, // but wait until later when we are connected to add it to the map. ConnectionInfo connectionInfo; if (!ownerToConnectionMap.TryGetValue(connectionParams.OwnerUri, out connectionInfo)) { connectionInfo = new ConnectionInfo(ConnectionFactory, connectionParams.OwnerUri, connectionParams.Connection); } // 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 = connectionParams.Type }; Disconnect(disconnectParams); } // Try to open a connection with the given ConnectParams ConnectionCompleteParams response = await TryOpenConnection(connectionInfo, connectionParams); if (response != null) { return(response); } // If this is the first connection for this URI, add the ConnectionInfo to the map if (!ownerToConnectionMap.ContainsKey(connectionParams.OwnerUri)) { ownerToConnectionMap[connectionParams.OwnerUri] = connectionInfo; } // Invoke callback notifications InvokeOnConnectionActivities(connectionInfo, connectionParams); // Return information about the connected SQL Server instance return(GetConnectionCompleteParams(connectionParams.Type, connectionInfo)); }
/// <summary> /// Creates a ConnectionCompleteParams as a response to a successful connection. /// Also sets the DatabaseName and IsAzure properties of ConnectionInfo. /// </summary> /// <returns>A ConnectionCompleteParams in response to the successful connection</returns> private ConnectionCompleteParams GetConnectionCompleteParams(string connectionType, ConnectionInfo connectionInfo) { ConnectionCompleteParams response = new ConnectionCompleteParams { OwnerUri = connectionInfo.OwnerUri, Type = connectionType }; try { DbConnection connection; connectionInfo.TryGetConnection(connectionType, out connection); // 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 = connection.Database; response.ConnectionSummary = new ConnectionSummary { ServerName = connectionInfo.ConnectionDetails.ServerName, DatabaseName = connectionInfo.ConnectionDetails.DatabaseName, UserName = connectionInfo.ConnectionDetails.UserName, }; response.ConnectionId = connectionInfo.ConnectionId.ToString(); var reliableConnection = connection as ReliableSqlConnection; DbConnection underlyingConnection = reliableConnection != null ? reliableConnection.GetUnderlyingConnection() : connection; ReliableConnectionHelper.ServerInfo serverInfo = ReliableConnectionHelper.GetServerVersion(underlyingConnection); 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; connectionInfo.MajorVersion = serverInfo.ServerMajorVersion; connectionInfo.IsSqlDW = (serverInfo.EngineEditionId == (int)DatabaseEngineEdition.SqlDataWarehouse); } catch (Exception ex) { response.Messages = ex.ToString(); } return(response); }
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider, ServerConnection serverConnection, bool isDefaultOrSystemDatabase) { ServerNode rootNode = new ServerNode(response, serviceProvider, serverConnection); var session = new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService <ConnectionService>()); if (!isDefaultOrSystemDatabase) { // Assuming the databases are in a folder under server node DatabaseTreeNode databaseNode = new DatabaseTreeNode(rootNode, response.ConnectionSummary.DatabaseName); session.Root = databaseNode; } return(session); }
private async Task <ServerConnection> ValidateAndCreateConnection(ConnectParams connectionParams) { // Validate Parameters and Create Connection ConnectionCompleteParams connectionCompleteParams = await ConnectionServiceInstance.Connect(connectionParams); if (!string.IsNullOrEmpty(connectionCompleteParams.Messages)) { throw new Exception(connectionCompleteParams.Messages); } // Get Connection ConnectionInfo connectionInfo = ConnectionServiceInstance.OwnerToConnectionMap[connectionParams.OwnerUri]; ServerConnection serverConn = ConnectionService.OpenServerConnection(connectionInfo); return(serverConn); }
public ServerNode(ConnectionCompleteParams connInfo, IMultiServiceProvider serviceProvider, ServerConnection serverConnection) : base() { Validate.IsNotNull(nameof(connInfo), connInfo); Validate.IsNotNull("connInfo.ConnectionSummary", connInfo.ConnectionSummary); Validate.IsNotNull(nameof(serviceProvider), serviceProvider); this.connectionSummary = connInfo.ConnectionSummary; this.serverInfo = connInfo.ServerInfo; this.sqlServerType = ServerVersionHelper.CalculateServerType(this.serverInfo); this.context = new Lazy <SmoQueryContext>(() => CreateContext(serviceProvider)); this.serverConnection = serverConnection; NodeValue = connectionSummary.ServerName; IsAlwaysLeaf = false; NodeType = NodeTypes.Server.ToString(); NodeTypeId = NodeTypes.Server; Label = GetConnectionLabel(); }
public NodeTests() { defaultServerInfo = TestObjects.GetTestServerInfo(); defaultConnectionDetails = new ConnectionDetails() { DatabaseName = "master", ServerName = "localhost", UserName = "******", Password = "******" }; defaultConnParams = new ConnectionCompleteParams() { ServerInfo = defaultServerInfo, ConnectionSummary = defaultConnectionDetails, OwnerUri = defaultOwnerUri }; // TODO can all tests use the standard service provider? ServiceProvider = ExtensionServiceProvider.CreateDefaultServiceProvider(); }
private void RunConnectRequestHandlerTask(ConnectParams connectParams) { // create a task to connect asynchronously so that other requests are not blocked in the meantime Task.Run(async() => { try { // open connection based on request details ConnectionCompleteParams result = await Instance.Connect(connectParams); await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result); } catch (Exception ex) { ConnectionCompleteParams result = new ConnectionCompleteParams() { Messages = ex.ToString() }; await ServiceHost.SendEvent(ConnectionCompleteNotification.Type, result); } }); }
public void ServerNodeLabelShouldIgnoreUserNameIfEmptyOrNull() { // Given no username set ConnectionSummary integratedAuthSummary = new ConnectionSummary() { DatabaseName = defaultConnectionDetails.DatabaseName, ServerName = defaultConnectionDetails.ServerName, UserName = null }; ConnectionCompleteParams connParams = new ConnectionCompleteParams() { ConnectionSummary = integratedAuthSummary, ServerInfo = defaultServerInfo, OwnerUri = defaultOwnerUri }; // When querying label string label = new ServerNode(connParams, ServiceProvider, serverConnection).Label; // Then only server name and version shown string expectedLabel = defaultConnectionDetails.ServerName + " (SQL Server " + defaultServerInfo.ServerVersion + ")"; Assert.Equal(expectedLabel, label); }
/// <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); }
/// <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); }
/// <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); }
public static ObjectExplorerSession CreateSession(ConnectionCompleteParams response, IMultiServiceProvider serviceProvider, ServerConnection serverConnection) { ServerNode rootNode = new ServerNode(response, serviceProvider, serverConnection); return(new ObjectExplorerSession(response.OwnerUri, rootNode, serviceProvider, serviceProvider.GetService <ConnectionService>())); }