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);
                }
            });
        }
Exemple #2
0
        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);
        }
Exemple #5
0
        /// <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);
        }
Exemple #8
0
            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);
            }
Exemple #9
0
        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);
        }
Exemple #10
0
        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();
        }
Exemple #11
0
        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);
         }
     });
 }
Exemple #13
0
        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);
        }
Exemple #17
0
            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>()));
            }