diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployPlanRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployPlanRequest.cs
new file mode 100644
index 00000000..56d914bc
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/Contracts/GenerateDeployPlanRequest.cs
@@ -0,0 +1,37 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+
+namespace Microsoft.SqlTools.ServiceLayer.DacFx.Contracts
+{
+ ///
+ /// Parameters for a DacFx generate deploy plan request.
+ ///
+ public class GenerateDeployPlanParams : DacFxParams
+ {
+ }
+
+ ///
+ /// Defines the DacFx generate deploy plan request type
+ ///
+ class GenerateDeployPlanRequest
+ {
+ public static readonly RequestType Type =
+ RequestType.Create("dacfx/generateDeployPlan");
+ }
+
+ ///
+ /// Parameters returned from a generate deploy script request.
+ ///
+ public class GenerateDeployPlanRequestResult : DacFxResult
+ {
+ ///
+ /// An xml string that details the alerts and the operations for deploying the specified dacpac to the database
+ ///
+ public string Report { get; set; }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
index e8037aff..1fbc443b 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxOperation.cs
@@ -90,5 +90,23 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
}
public abstract void Execute();
+
+ protected DacDeployOptions GetDefaultDeployOptions()
+ {
+ DacDeployOptions options = new DacDeployOptions
+ {
+ AllowDropBlockingAssemblies = true,
+ AllowIncompatiblePlatform = true,
+ BlockOnPossibleDataLoss = false,
+ DropObjectsNotInSource = true,
+ DropPermissionsNotInSource = true,
+ DropRoleMembersNotInSource = true,
+ IgnoreKeywordCasing = false,
+ IgnoreSemicolonBetweenStatements = false,
+ IgnoreWhitespace = false,
+ };
+
+ return options;
+ }
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
index 7ae9b2d4..874452d3 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DacFxService.cs
@@ -44,6 +44,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
serviceHost.SetRequestHandler(ExtractRequest.Type, this.HandleExtractRequest);
serviceHost.SetRequestHandler(DeployRequest.Type, this.HandleDeployRequest);
serviceHost.SetRequestHandler(GenerateDeployScriptRequest.Type, this.HandleGenerateDeployScriptRequest);
+ serviceHost.SetRequestHandler(GenerateDeployPlanRequest.Type, this.HandleGenerateDeployPlanRequest);
}
///
@@ -167,7 +168,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
// want to show filepath in task history instead of server and database
metadata.ServerName = parameters.ScriptFilePath;
- metadata.DatabaseName = "";
+ metadata.DatabaseName = string.Empty;
sqlTask = SqlTaskManagerInstance.CreateAndRun(metadata);
@@ -175,7 +176,39 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
OperationId = operation.OperationId,
Success = true,
- ErrorMessage = ""
+ 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
});
}
}
@@ -199,7 +232,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
{
OperationId = operation.OperationId,
Success = true,
- ErrorMessage = ""
+ ErrorMessage = string.Empty
});
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
index f6d07bba..50672b0a 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/DeployOperation.cs
@@ -29,7 +29,8 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
public override void Execute()
{
DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
- this.DacServices.Deploy(dacpac, this.Parameters.DatabaseName, this.Parameters.UpgradeExisting, null, this.CancellationToken);
+ DacDeployOptions options = this.GetDefaultDeployOptions();
+ this.DacServices.Deploy(dacpac, this.Parameters.DatabaseName, this.Parameters.UpgradeExisting, options, this.CancellationToken);
}
}
}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployPlanOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployPlanOperation.cs
new file mode 100644
index 00000000..e72bf8d9
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployPlanOperation.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+using Microsoft.SqlServer.Dac;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.Utility;
+using System;
+using System.Data.SqlClient;
+using System.Diagnostics;
+
+namespace Microsoft.SqlTools.ServiceLayer.DacFx
+{
+ ///
+ /// Class to represent an in-progress generate deploy plan operation
+ ///
+ class GenerateDeployPlanOperation : DacFxOperation
+ {
+ public GenerateDeployPlanParams Parameters { get; }
+
+ public string DeployReport { get; set; }
+
+ public GenerateDeployPlanOperation(GenerateDeployPlanParams parameters, ConnectionInfo connInfo): base(connInfo)
+ {
+ Validate.IsNotNull("parameters", parameters);
+ this.Parameters = parameters;
+ }
+
+ public override void Execute()
+ {
+ DacPackage dacpac = DacPackage.Load(this.Parameters.PackageFilePath);
+ DacDeployOptions options = GetDefaultDeployOptions();
+ DeployReport = this.DacServices.GenerateDeployReport(dacpac, this.Parameters.DatabaseName, options, this.CancellationToken);
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
index 0ef7bee1..d68cab29 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/DacFx/GenerateDeployScriptOperation.cs
@@ -33,6 +33,7 @@ namespace Microsoft.SqlTools.ServiceLayer.DacFx
PublishOptions publishOptions = new PublishOptions();
publishOptions.GenerateDeploymentReport = this.Parameters.GenerateDeploymentReport;
publishOptions.CancelToken = this.CancellationToken;
+ publishOptions.DeployOptions = this.GetDefaultDeployOptions();
publishOptions.DatabaseScriptPath = this.Parameters.ScriptFilePath;
// master script is only used if the target is Azure SQL db and the script contains all operations that must be done against the master database
publishOptions.MasterDbScriptPath = Path.Combine(Path.GetDirectoryName(this.Parameters.ScriptFilePath), string.Concat("master_", Path.GetFileName(this.Parameters.ScriptFilePath)));
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
index d97ce325..33d5dcad 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
@@ -199,7 +199,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
{
service.PerformOperation(operation);
}
- catch(OperationCanceledException canceledException)
+ catch (OperationCanceledException canceledException)
{
expectedException = canceledException;
}
@@ -260,6 +260,74 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
return generateScriptRequestContext;
}
+ private async Task>> SendAndValidateGenerateDeployPlanRequest()
+ {
+ var result = GetLiveAutoCompleteTestObjects();
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "DacFxGenerateDeployPlanTest");
+ sourceDb.RunQuery(@"CREATE TABLE [dbo].[table1]
+(
+ [ID] INT NOT NULL PRIMARY KEY,
+ [Date] DATE NOT NULL
+)
+CREATE TABLE [dbo].[table2]
+(
+ [ID] INT NOT NULL PRIMARY KEY,
+ [col1] NCHAR(10) NULL
+)");
+ DacFxService service = new DacFxService();
+ string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
+ Directory.CreateDirectory(folderPath);
+
+ var extractParams = new ExtractParams
+ {
+ DatabaseName = sourceDb.DatabaseName,
+ PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", sourceDb.DatabaseName)),
+ ApplicationName = "test",
+ ApplicationVersion = new Version(1, 0)
+ };
+
+ ExtractOperation extractOperation = new ExtractOperation(extractParams, result.ConnectionInfo);
+ service.PerformOperation(extractOperation);
+
+ // generate deploy plan for deploying dacpac to targetDb
+ var generateDeployPlanRequestContext = new Mock>();
+ generateDeployPlanRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+ SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, null, "DacFxGenerateDeployPlanTestTarget");
+ targetDb.RunQuery(@"CREATE TABLE [dbo].[table2]
+(
+ [ID] INT NOT NULL PRIMARY KEY,
+ [col1] NCHAR(10) NULL,
+ [col2] NCHAR(10) NULL
+)
+CREATE TABLE [dbo].[table3]
+(
+ [ID] INT NOT NULL PRIMARY KEY,
+ [col1] INT NULL,
+)");
+
+ var generateDeployPlanParams = new GenerateDeployPlanParams
+ {
+ PackageFilePath = extractParams.PackageFilePath,
+ DatabaseName = targetDb.DatabaseName,
+ };
+
+ GenerateDeployPlanOperation generateDeployPlanOperation = new GenerateDeployPlanOperation(generateDeployPlanParams, result.ConnectionInfo);
+ service.PerformOperation(generateDeployPlanOperation);
+ string report = generateDeployPlanOperation.DeployReport;
+ Assert.NotNull(report);
+ Assert.Contains("Create", report);
+ Assert.Contains("Drop", report);
+ Assert.Contains("Alter", report);
+
+ // cleanup
+ VerifyAndCleanup(extractParams.PackageFilePath);
+ sourceDb.Cleanup();
+ targetDb.Cleanup();
+
+ return generateDeployPlanRequestContext;
+ }
+
///
/// Verify the export bacpac request
///
@@ -306,7 +374,7 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
}
///
- /// Verify the gnerate deploy script request
+ /// Verify the generate deploy script request
///
[Fact]
public async void GenerateDeployScript()
@@ -314,6 +382,15 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
Assert.NotNull(await SendAndValidateGenerateDeployScriptRequest());
}
+ ///
+ /// Verify the generate deploy plan request
+ ///
+ [Fact]
+ public async void GenerateDeployPlan()
+ {
+ Assert.NotNull(await SendAndValidateGenerateDeployPlanRequest());
+ }
+
private void VerifyAndCleanup(string filePath)
{
// Verify it was created