// // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. // using System; using System.Collections.Concurrent; using System.Threading.Tasks; using Microsoft.SqlServer.Dac; using Microsoft.SqlTools.Hosting.Protocol; using Microsoft.SqlTools.ServiceLayer.Connection; using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts; using Microsoft.SqlTools.ServiceLayer.Hosting; using Microsoft.SqlTools.ServiceLayer.TaskServices; using DacTableDesigner = Microsoft.Data.Tools.Sql.DesignServices.TableDesigner.TableDesigner; namespace Microsoft.SqlTools.ServiceLayer.DacFx { /// /// Main class for DacFx service /// class DacFxService { private static ConnectionService connectionService = null; private SqlTaskManager sqlTaskManagerInstance = null; private static readonly Lazy instance = new Lazy(() => new DacFxService()); private readonly Lazy> operations = new Lazy>(() => new ConcurrentDictionary()); /// /// Gets the singleton instance object /// public static DacFxService Instance { get { return instance.Value; } } /// /// Initializes the service instance /// /// public void InitializeService(ServiceHost serviceHost) { serviceHost.SetRequestHandler(ExportRequest.Type, this.HandleExportRequest); serviceHost.SetRequestHandler(ImportRequest.Type, this.HandleImportRequest); serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest); serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest); serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest); serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest); serviceHost.SetRequestHandler(GetOptionsFromProfileRequest.Type, this.HandleGetOptionsFromProfileRequest); serviceHost.SetRequestHandler(ValidateStreamingJobRequest.Type, this.HandleValidateStreamingJobRequest); serviceHost.SetRequestHandler(GetDefaultPublishOptionsRequest.Type, this.HandleGetDefaultPublishOptionsRequest); serviceHost.SetRequestHandler(ParseTSqlScriptRequest.Type, this.HandleParseTSqlScriptRequest); } /// /// The collection of active operations /// internal ConcurrentDictionary ActiveOperations => operations.Value; /// /// Handles request to export a bacpac /// /// public async Task HandleExportRequest(ExportParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { ExportOperation operation = new ExportOperation(parameters, connInfo); ExecuteOperation(operation, parameters, SR.ExportBacpacTaskName, requestContext); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to import a bacpac /// /// public async Task HandleImportRequest(ImportParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { ImportOperation operation = new ImportOperation(parameters, connInfo); ExecuteOperation(operation, parameters, SR.ImportBacpacTaskName, requestContext); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to extract a dacpac /// /// public async Task HandleExtractRequest(ExtractParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { // Set connection details database name to ensure the connection string gets created correctly for DW(extract doesn't work if connection is to master) connInfo.ConnectionDetails.DatabaseName = parameters.DatabaseName; ExtractOperation operation = new ExtractOperation(parameters, connInfo); string taskName = parameters.ExtractTarget == DacExtractTarget.DacPac ? SR.ExtractDacpacTaskName : SR.ProjectExtractTaskName; ExecuteOperation(operation, parameters, taskName, requestContext); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to deploy a dacpac /// /// public async Task HandleDeployRequest(DeployParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { DeployOperation operation = new DeployOperation(parameters, connInfo); ExecuteOperation(operation, parameters, SR.DeployDacpacTaskName, requestContext); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to generate deploy script /// /// public async Task HandleGenerateDeployScriptRequest(GenerateDeployScriptParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { GenerateDeployScriptOperation operation = new GenerateDeployScriptOperation(parameters, connInfo); SqlTask sqlTask = null; TaskMetadata metadata = new TaskMetadata(); metadata.TaskOperation = operation; metadata.TaskExecutionMode = parameters.TaskExecutionMode; metadata.ServerName = connInfo.ConnectionDetails.ServerName; metadata.DatabaseName = parameters.DatabaseName; metadata.Name = SR.GenerateScriptTaskName; sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata); await requestContext.SendResult(new DacFxResult() { OperationId = operation.OperationId, Success = true, ErrorMessage = string.Empty }); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to generate deploy plan /// /// public async Task HandleGenerateDeployPlanRequest(GenerateDeployPlanParams parameters, RequestContext requestContext) { try { ConnectionInfo connInfo; ConnectionServiceInstance.TryFindConnection( parameters.OwnerUri, out connInfo); if (connInfo != null) { GenerateDeployPlanOperation operation = new GenerateDeployPlanOperation(parameters, connInfo); operation.Execute(parameters.TaskExecutionMode); await requestContext.SendResult(new GenerateDeployPlanRequestResult() { OperationId = operation.OperationId, Success = true, ErrorMessage = string.Empty, Report = operation.DeployReport }); } } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to get the options from a publish profile /// /// public async Task HandleGetOptionsFromProfileRequest(GetOptionsFromProfileParams parameters, RequestContext requestContext) { try { DeploymentOptions options = null; if (parameters.ProfilePath != null) { DacProfile profile = DacProfile.Load(parameters.ProfilePath); if (profile.DeployOptions != null) { options = DeploymentOptions.GetDefaultPublishOptions(); await options.InitializeFromProfile(profile.DeployOptions, parameters.ProfilePath); } } await requestContext.SendResult(new DacFxOptionsResult() { DeploymentOptions = options, Success = true, ErrorMessage = string.Empty, }); } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to validate an ASA streaming job /// /// public async Task HandleValidateStreamingJobRequest(ValidateStreamingJobParams parameters, RequestContext requestContext) { try { ValidateStreamingJobOperation operation = new ValidateStreamingJobOperation(parameters); ValidateStreamingJobResult result = operation.ValidateQuery(); await requestContext.SendResult(result); } catch (Exception e) { await requestContext.SendError(e); } } /// /// Handles request to create default publish options for DacFx /// /// public async Task HandleGetDefaultPublishOptionsRequest(GetDefaultPublishOptionsParams parameters, RequestContext requestContext) { try { // this does not need to be an async operation since this only creates and returns the default object DeploymentOptions options = DeploymentOptions.GetDefaultPublishOptions(); await requestContext.SendResult(new DacFxOptionsResult() { DeploymentOptions = options, Success = true, ErrorMessage = null }); } catch (Exception e) { await requestContext.SendResult(new DacFxOptionsResult() { DeploymentOptions = null, Success = false, ErrorMessage = e.Message }); } } public async Task HandleParseTSqlScriptRequest(ParseTSqlScriptRequestParams requestParams, RequestContext requestContext) { try { var script = System.IO.File.ReadAllText(requestParams.FilePath); await requestContext.SendResult(new ParseTSqlScriptResult() { ContainsCreateTableStatement = DacTableDesigner.ScriptContainsCreateTableStatements(script, requestParams.DatabaseSchemaProvider) }); } catch (Exception e) { await requestContext.SendError(e); } } private void ExecuteOperation(DacFxOperation operation, DacFxParams parameters, string taskName, RequestContext requestContext) { Task.Run(async () => { try { // show file location for export and extract operations string targetLocation = (operation is ExportOperation || operation is ExtractOperation) ? parameters.PackageFilePath : null; TaskMetadata metadata = TaskMetadata.Create(parameters, taskName, operation, ConnectionServiceInstance, targetLocation); // put appropriate database name since connection passed was to master metadata.DatabaseName = parameters.DatabaseName; SqlTask sqlTask = SqlTaskManagerInstance.CreateTask(metadata); await sqlTask.RunAsync(); await requestContext.SendResult(new DacFxResult() { OperationId = operation.OperationId, Success = sqlTask.TaskStatus == SqlTaskStatus.Succeeded, ErrorMessage = string.Empty }); } catch (Exception e) { await requestContext.SendResult(new DacFxResult() { OperationId = operation.OperationId, Success = false, ErrorMessage = e.Message }); } }); } private SqlTaskManager SqlTaskManagerInstance { get { if (sqlTaskManagerInstance == null) { sqlTaskManagerInstance = SqlTaskManager.Instance; } return sqlTaskManagerInstance; } set { sqlTaskManagerInstance = value; } } /// /// Internal for testing purposes only /// internal static ConnectionService ConnectionServiceInstance { get { if (connectionService == null) { connectionService = ConnectionService.Instance; } return connectionService; } set { connectionService = value; } } /// /// For testing purpose only /// /// internal void PerformOperation(DacFxOperation operation, TaskExecutionMode taskExecutionMode) { operation.Execute(taskExecutionMode); } } }