예제 #1
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();

            response.OwnerUri = connectionParams.OwnerUri;
            CancellationTokenSource source = null;

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

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

                // turning on MARS to avoid break in LanguageService with multiple editors
                // we'll remove this once ConnectionService is refactored to not own the LanguageService connection
                connectionInfo.ConnectionDetails.MultipleActiveResultSets = true;

                // 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 Contracts.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
                };
            }
            catch (Exception ex)
            {
                response.Messages = ex.ToString();
            }

            // return the connection result
            response.ConnectionId = connectionInfo.ConnectionId.ToString();
            return(response);
        }