diff --git a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
index 7239dc66..d7b47456 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/HostLoader.cs
@@ -21,6 +21,7 @@ using Microsoft.SqlTools.ServiceLayer.LanguageServices;
using Microsoft.SqlTools.ServiceLayer.Metadata;
using Microsoft.SqlTools.ServiceLayer.Profiler;
using Microsoft.SqlTools.ServiceLayer.QueryExecution;
+using Microsoft.SqlTools.ServiceLayer.SchemaCopmare;
using Microsoft.SqlTools.ServiceLayer.Scripting;
using Microsoft.SqlTools.ServiceLayer.Security;
using Microsoft.SqlTools.ServiceLayer.SqlContext;
@@ -118,6 +119,9 @@ namespace Microsoft.SqlTools.ServiceLayer
CmsService.Instance.InitializeService(serviceHost);
serviceProvider.RegisterSingleService(CmsService.Instance);
+ SchemaCopmare.SchemaCompareService.Instance.InitializeService(serviceHost);
+ serviceProvider.RegisterSingleService(SchemaCompareService.Instance);
+
InitializeHostedServices(serviceProvider, serviceHost);
serviceHost.ServiceProvider = serviceProvider;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
index 1e49d364..cb0e88d4 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
+++ b/src/Microsoft.SqlTools.ServiceLayer/Microsoft.SqlTools.ServiceLayer.csproj
@@ -23,7 +23,7 @@
-
+
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs
new file mode 100644
index 00000000..0cd43561
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/Contracts/SchemaCompareRequest.cs
@@ -0,0 +1,96 @@
+//
+// 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.Compare;
+using Microsoft.SqlTools.Hosting.Protocol.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.ServiceLayer.Utility;
+using System.Collections.Generic;
+
+namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts
+{
+ public enum SchemaCompareEndpointType
+ {
+ Database,
+ Dacpac
+ }
+
+ public class SchemaCompareEndpointInfo
+ {
+ ///
+ /// Gets or sets the type of the endpoint
+ ///
+ public SchemaCompareEndpointType EndpointType { get; set; }
+
+ ///
+ /// Gets or sets package filepath
+ ///
+ public string PackageFilePath { get; set; }
+
+ ///
+ /// Gets or sets name for the database
+ ///
+ public string DatabaseName { get; set; }
+
+ ///
+ /// Connection uri
+ ///
+ public string OwnerUri { get; set; }
+ }
+
+ ///
+ /// Parameters for a schema compare request.
+ ///
+ public class SchemaCompareParams
+ {
+ ///
+ /// Gets or sets the source endpoint info
+ ///
+ public SchemaCompareEndpointInfo SourceEndpointInfo { get; set; }
+
+ ///
+ /// Gets or sets the target endpoint info
+ ///
+ public SchemaCompareEndpointInfo TargetEndpointInfo { get; set; }
+
+ ///
+ /// Executation mode for the operation. Default is execution
+ ///
+ public TaskExecutionMode TaskExecutionMode { get; set; }
+ }
+
+ ///
+ /// Parameters returned from a schema compare request.
+ ///
+ public class SchemaCompareResult : ResultStatus
+ {
+ public string OperationId { get; set; }
+
+ public bool AreEqual { get; set; }
+
+ public List Differences { get; set; }
+ }
+
+ public class DiffEntry
+ {
+ public SchemaUpdateAction UpdateAction;
+ public SchemaDifferenceType DifferenceType;
+ public string Name;
+ public string SourceValue;
+ public string TargetValue;
+ public DiffEntry Parent;
+ public List Children;
+ public string SourceScript;
+ public string TargetScript;
+ }
+
+ ///
+ /// Defines the Schema Compare request type
+ ///
+ class SchemaCompareRequest
+ {
+ public static readonly RequestType Type =
+ RequestType.Create("schemaCompare/compare");
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs
new file mode 100644
index 00000000..15b37da6
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareOperation.cs
@@ -0,0 +1,203 @@
+//
+// 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.Compare;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.Utility;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using System.Threading;
+
+namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
+{
+ ///
+ /// Schema compare operation
+ ///
+ class SchemaCompareOperation : ITaskOperation
+ {
+ private CancellationTokenSource cancellation = new CancellationTokenSource();
+ private bool disposed = false;
+
+ ///
+ /// Gets the unique id associated with this instance.
+ ///
+ public string OperationId { get; private set; }
+
+ public SqlTask SqlTask { get; set; }
+
+ public SchemaCompareParams Parameters { get; set; }
+
+ public string SourceConnectionString { get; set; }
+
+ public string TargetConnectionString { get; set; }
+
+ public SchemaComparisonResult ComparisonResult { get; set; }
+
+ public List Differences;
+
+ public SchemaCompareOperation(SchemaCompareParams parameters, ConnectionInfo sourceConnInfo, ConnectionInfo targetConnInfo)
+ {
+ Validate.IsNotNull("parameters", parameters);
+ this.Parameters = parameters;
+ this.SourceConnectionString = GetConnectionString(sourceConnInfo, parameters.SourceEndpointInfo.DatabaseName);
+ this.TargetConnectionString = GetConnectionString(targetConnInfo, parameters.TargetEndpointInfo.DatabaseName);
+ this.OperationId = Guid.NewGuid().ToString();
+ }
+
+ protected CancellationToken CancellationToken { get { return this.cancellation.Token; } }
+
+ ///
+ /// The error occurred during operation
+ ///
+ public string ErrorMessage { get; set; }
+
+ ///
+ /// Cancel operation
+ ///
+ public void Cancel()
+ {
+ if (!this.cancellation.IsCancellationRequested)
+ {
+ Logger.Write(TraceEventType.Verbose, string.Format("Cancel invoked for OperationId {0}", this.OperationId));
+ this.cancellation.Cancel();
+ }
+ }
+
+ ///
+ /// Disposes the operation.
+ ///
+ public void Dispose()
+ {
+ if (!disposed)
+ {
+ this.Cancel();
+ disposed = true;
+ }
+ }
+
+ public void Execute(TaskExecutionMode mode)
+ {
+ if (this.CancellationToken.IsCancellationRequested)
+ {
+ throw new OperationCanceledException(this.CancellationToken);
+ }
+
+ try
+ {
+ SchemaCompareEndpoint sourceEndpoint = CreateSchemaCompareEndpoint(this.Parameters.SourceEndpointInfo, this.SourceConnectionString);
+ SchemaCompareEndpoint targetEndpoint = CreateSchemaCompareEndpoint(this.Parameters.TargetEndpointInfo, this.TargetConnectionString);
+
+ SchemaComparison comparison = new SchemaComparison(sourceEndpoint, targetEndpoint);
+ this.ComparisonResult = comparison.Compare();
+
+ // try one more time if it didn't work the first time
+ if(!this.ComparisonResult.IsValid)
+ {
+ this.ComparisonResult = comparison.Compare();
+ }
+
+ this.Differences = new List();
+ foreach (SchemaDifference difference in this.ComparisonResult.Differences)
+ {
+ DiffEntry diffEntry = CreateDiffEntry(difference, null);
+ this.Differences.Add(diffEntry);
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.Write(TraceEventType.Error, string.Format("Schema compare operation {0} failed with exception {1}", this.OperationId, e));
+ throw;
+ }
+ }
+
+ private DiffEntry CreateDiffEntry(SchemaDifference difference, DiffEntry parent)
+ {
+ DiffEntry diffEntry = new DiffEntry();
+ diffEntry.UpdateAction = difference.UpdateAction;
+ diffEntry.DifferenceType = difference.DifferenceType;
+ diffEntry.Name = difference.Name;
+
+ if (difference.SourceObject != null)
+ {
+ diffEntry.SourceValue = GetName(difference.SourceObject.Name.ToString());
+ }
+ if (difference.TargetObject != null)
+ {
+ diffEntry.TargetValue = GetName(difference.TargetObject.Name.ToString());
+ }
+
+ if (difference.DifferenceType == SchemaDifferenceType.Object)
+ {
+ // set source and target scripts
+ if (difference.SourceObject != null)
+ {
+ string sourceScript;
+ difference.SourceObject.TryGetScript(out sourceScript);
+ diffEntry.SourceScript = RemoveExcessWhitespace(sourceScript);
+ }
+ if (difference.TargetObject != null)
+ {
+ string targetScript;
+ difference.TargetObject.TryGetScript(out targetScript);
+ diffEntry.TargetScript = RemoveExcessWhitespace(targetScript);
+ }
+ }
+
+ diffEntry.Children = new List();
+
+ foreach (SchemaDifference child in difference.Children)
+ {
+ diffEntry.Children.Add(CreateDiffEntry(child, diffEntry));
+ }
+
+ return diffEntry;
+ }
+
+ private SchemaCompareEndpoint CreateSchemaCompareEndpoint(SchemaCompareEndpointInfo endpointInfo, string connectionString)
+ {
+ switch (endpointInfo.EndpointType)
+ {
+ case SchemaCompareEndpointType.Dacpac:
+ {
+ return new SchemaCompareDacpacEndpoint(endpointInfo.PackageFilePath);
+ }
+ case SchemaCompareEndpointType.Database:
+ {
+ return new SchemaCompareDatabaseEndpoint(connectionString);
+ }
+ default:
+ {
+ return null;
+ }
+ }
+ }
+
+ private string GetConnectionString(ConnectionInfo connInfo, string databaseName)
+ {
+ if (connInfo == null)
+ {
+ return null;
+ }
+
+ connInfo.ConnectionDetails.DatabaseName = databaseName;
+ return ConnectionService.BuildConnectionString(connInfo.ConnectionDetails);
+ }
+
+ private string RemoveExcessWhitespace(string script)
+ {
+ // replace all multiple spaces with single space
+ return Regex.Replace(script, " {2,}", " ");
+ }
+
+ private string GetName(string name)
+ {
+ // remove brackets from name
+ return Regex.Replace(name, @"[\[\]]", "");
+ }
+ }
+}
diff --git a/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs
new file mode 100644
index 00000000..d3cfa5af
--- /dev/null
+++ b/src/Microsoft.SqlTools.ServiceLayer/SchemaCompare/SchemaCompareService.cs
@@ -0,0 +1,104 @@
+//
+// 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;
+using Microsoft.SqlTools.ServiceLayer.Connection;
+using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
+using Microsoft.SqlTools.ServiceLayer.Hosting;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using System;
+using System.Collections.Concurrent;
+using System.Data.SqlClient;
+using System.Threading.Tasks;
+using Microsoft.SqlTools.ServiceLayer.SchemaCompare;
+
+namespace Microsoft.SqlTools.ServiceLayer.SchemaCopmare
+{
+ ///
+ /// Main class for SchemaCompare service
+ ///
+ class SchemaCompareService
+ {
+ private static ConnectionService connectionService = null;
+ private static readonly Lazy instance = new Lazy(() => new SchemaCompareService());
+
+ ///
+ /// Gets the singleton instance object
+ ///
+ public static SchemaCompareService Instance
+ {
+ get { return instance.Value; }
+ }
+
+ ///
+ /// Initializes the service instance
+ ///
+ ///
+ public void InitializeService(ServiceHost serviceHost)
+ {
+ serviceHost.SetRequestHandler(SchemaCompareRequest.Type, this.HandleSchemaCompareRequest);
+ }
+
+ ///
+ /// Handles schema compare request
+ ///
+ ///
+ public async Task HandleSchemaCompareRequest(SchemaCompareParams parameters, RequestContext requestContext)
+ {
+ try
+ {
+ ConnectionInfo sourceConnInfo;
+ ConnectionInfo targetConnInfo;
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.SourceEndpointInfo.OwnerUri,
+ out sourceConnInfo);
+ ConnectionServiceInstance.TryFindConnection(
+ parameters.TargetEndpointInfo.OwnerUri,
+ out targetConnInfo);
+
+ Task schemaCompareTask = Task.Run(async () =>
+ {
+ try
+ {
+ SchemaCompareOperation operation = new SchemaCompareOperation(parameters, sourceConnInfo, targetConnInfo);
+ operation.Execute(parameters.TaskExecutionMode);
+
+ await requestContext.SendResult(new SchemaCompareResult()
+ {
+ OperationId = operation.OperationId,
+ Success = true,
+ ErrorMessage = operation.ErrorMessage,
+ AreEqual = operation.ComparisonResult.IsEqual,
+ Differences = operation.Differences
+ });
+ }
+ catch(Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ });
+ }
+ catch (Exception e)
+ {
+ await requestContext.SendError(e);
+ }
+ }
+
+ internal static ConnectionService ConnectionServiceInstance
+ {
+ get
+ {
+ if (connectionService == null)
+ {
+ connectionService = ConnectionService.Instance;
+ }
+ return connectionService;
+ }
+ set
+ {
+ connectionService = value;
+ }
+ }
+ }
+}
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
index 33d5dcad..780bf3ef 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/DacFx/DacFxserviceTests.cs
@@ -3,14 +3,13 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using Microsoft.SqlTools.Hosting.Protocol;
-using Microsoft.SqlTools.ServiceLayer.Connection;
using Microsoft.SqlTools.ServiceLayer.DacFx;
using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
using Microsoft.SqlTools.ServiceLayer.Test.Common;
using Moq;
using System;
-using System.Data.SqlClient;
using System.IO;
using System.Threading.Tasks;
using Xunit;
@@ -19,6 +18,28 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
{
public class DacFxServiceTests
{
+ private const string SourceScript = @"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
+)";
+
+ private const string TargetScript = @"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,
+)";
private LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects()
{
@@ -263,17 +284,8 @@ namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.DacFx
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
-)");
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceScript, "DacFxGenerateDeployPlanTest");
+
DacFxService service = new DacFxService();
string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
Directory.CreateDirectory(folderPath);
@@ -293,18 +305,7 @@ CREATE TABLE [dbo].[table2]
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,
-)");
+ SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "DacFxGenerateDeployPlanTestTarget");
var generateDeployPlanParams = new GenerateDeployPlanParams
{
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Microsoft.SqlTools.ServiceLayer.IntegrationTests.csproj b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Microsoft.SqlTools.ServiceLayer.IntegrationTests.csproj
index c246c75c..803dafe2 100644
--- a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Microsoft.SqlTools.ServiceLayer.IntegrationTests.csproj
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/Microsoft.SqlTools.ServiceLayer.IntegrationTests.csproj
@@ -35,7 +35,7 @@
-
+
diff --git a/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs
new file mode 100644
index 00000000..8f3d13c6
--- /dev/null
+++ b/test/Microsoft.SqlTools.ServiceLayer.IntegrationTests/SchemaCompare/SchemaCompareServiceTests.cs
@@ -0,0 +1,235 @@
+//
+// 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;
+using Microsoft.SqlTools.ServiceLayer.DacFx;
+using Microsoft.SqlTools.ServiceLayer.DacFx.Contracts;
+using Microsoft.SqlTools.ServiceLayer.IntegrationTests.Utility;
+using Microsoft.SqlTools.ServiceLayer.SchemaCompare;
+using Microsoft.SqlTools.ServiceLayer.SchemaCompare.Contracts;
+using Microsoft.SqlTools.ServiceLayer.TaskServices;
+using Microsoft.SqlTools.ServiceLayer.Test.Common;
+using Moq;
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace Microsoft.SqlTools.ServiceLayer.IntegrationTests.SchemaCompare
+{
+ public class SchemaCompareServiceTests
+ {
+ private const string SourceScript = @"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
+)";
+
+ private const string TargetScript = @"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,
+)";
+
+ private LiveConnectionHelper.TestConnectionResult GetLiveAutoCompleteTestObjects()
+ {
+ var result = LiveConnectionHelper.InitLiveConnectionInfo();
+ return result;
+ }
+
+ private async Task>> SendAndValidateSchemaCompareRequestDacpacToDacpac()
+ {
+ var result = GetLiveAutoCompleteTestObjects();
+ var schemaCompareRequestContext = new Mock>();
+ schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+ // create dacpacs from databases
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceScript, "SchemaCompareSource");
+ SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "SchemaCompareTarget");
+ string sourceDacpacFilePath = CreateDacpac(sourceDb);
+ string targetDacpacFilePath = CreateDacpac(targetDb);
+
+ SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo();
+ SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo();
+
+ sourceInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
+ sourceInfo.PackageFilePath = sourceDacpacFilePath;
+ targetInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
+ targetInfo.PackageFilePath = targetDacpacFilePath;
+
+ var schemaCompareParams = new SchemaCompareParams
+ {
+ SourceEndpointInfo = sourceInfo,
+ TargetEndpointInfo = targetInfo
+ };
+
+ DacFxService service = new DacFxService();
+ SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, null, null);
+ schemaCompareOperation.Execute(TaskExecutionMode.Execute);
+
+ Assert.True(schemaCompareOperation.ComparisonResult.IsValid);
+ Assert.False(schemaCompareOperation.ComparisonResult.IsEqual);
+ Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences);
+
+ // cleanup
+ VerifyAndCleanup(sourceDacpacFilePath);
+ VerifyAndCleanup(targetDacpacFilePath);
+ sourceDb.Cleanup();
+ targetDb.Cleanup();
+
+ return schemaCompareRequestContext;
+ }
+
+ private async Task>> SendAndValidateSchemaCompareRequestDatabaseToDatabase()
+ {
+ var result = GetLiveAutoCompleteTestObjects();
+ var schemaCompareRequestContext = new Mock>();
+ schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceScript, "SchemaCompareSource");
+ SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "SchemaCompareTarget");
+ string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
+ Directory.CreateDirectory(folderPath);
+
+ SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo();
+ SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo();
+
+ sourceInfo.EndpointType = SchemaCompareEndpointType.Database;
+ sourceInfo.DatabaseName = sourceDb.DatabaseName;
+ targetInfo.EndpointType = SchemaCompareEndpointType.Database;
+ targetInfo.DatabaseName = targetDb.DatabaseName;
+
+ var schemaCompareParams = new SchemaCompareParams
+ {
+ SourceEndpointInfo = sourceInfo,
+ TargetEndpointInfo = targetInfo
+ };
+
+ DacFxService service = new DacFxService();
+ SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, result.ConnectionInfo);
+ schemaCompareOperation.Execute(TaskExecutionMode.Execute);
+
+ Assert.True(schemaCompareOperation.ComparisonResult.IsValid);
+ Assert.False(schemaCompareOperation.ComparisonResult.IsEqual);
+ Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences);
+
+ // cleanup
+ sourceDb.Cleanup();
+ targetDb.Cleanup();
+
+ return schemaCompareRequestContext;
+ }
+
+ private async Task>> SendAndValidateSchemaCompareRequestDatabaseToDacpac()
+ {
+ var result = GetLiveAutoCompleteTestObjects();
+ var schemaCompareRequestContext = new Mock>();
+ schemaCompareRequestContext.Setup(x => x.SendResult(It.IsAny())).Returns(Task.FromResult(new object()));
+
+ SqlTestDb sourceDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, SourceScript, "SchemaCompareSource");
+ SqlTestDb targetDb = await SqlTestDb.CreateNewAsync(TestServerType.OnPrem, false, null, TargetScript, "SchemaCompareTarget");
+ string targetDacpacFilePath = CreateDacpac(targetDb);
+
+ SchemaCompareEndpointInfo sourceInfo = new SchemaCompareEndpointInfo();
+ SchemaCompareEndpointInfo targetInfo = new SchemaCompareEndpointInfo();
+
+ sourceInfo.EndpointType = SchemaCompareEndpointType.Database;
+ sourceInfo.DatabaseName = sourceDb.DatabaseName;
+ targetInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
+ targetInfo.PackageFilePath = targetDacpacFilePath;
+
+ var schemaCompareParams = new SchemaCompareParams
+ {
+ SourceEndpointInfo = sourceInfo,
+ TargetEndpointInfo = targetInfo
+ };
+
+ DacFxService service = new DacFxService();
+ SchemaCompareOperation schemaCompareOperation = new SchemaCompareOperation(schemaCompareParams, result.ConnectionInfo, null);
+ schemaCompareOperation.Execute(TaskExecutionMode.Execute);
+
+ Assert.True(schemaCompareOperation.ComparisonResult.IsValid);
+ Assert.False(schemaCompareOperation.ComparisonResult.IsEqual);
+ Assert.NotNull(schemaCompareOperation.ComparisonResult.Differences);
+
+ // cleanup
+ VerifyAndCleanup(targetDacpacFilePath);
+ sourceDb.Cleanup();
+ targetDb.Cleanup();
+
+ return schemaCompareRequestContext;
+ }
+
+ ///
+ /// Verify the schema compare request comparing two dacpacs
+ ///
+ [Fact]
+ public void SchemaCompareDacpacToDacpac()
+ {
+ Assert.NotNull(SendAndValidateSchemaCompareRequestDacpacToDacpac());
+ }
+
+ ///
+ /// Verify the schema compare request comparing a two databases
+ ///
+ [Fact]
+ public async void SchemaCompareDatabaseToDatabase()
+ {
+ Assert.NotNull(await SendAndValidateSchemaCompareRequestDatabaseToDatabase());
+ }
+
+ ///
+ /// Verify the schema compare request comparing a database to a dacpac
+ ///
+ [Fact]
+ public async void SchemaCompareDatabaseToDacpac()
+ {
+ Assert.NotNull(await SendAndValidateSchemaCompareRequestDatabaseToDacpac());
+ }
+
+ private void VerifyAndCleanup(string filePath)
+ {
+ // Verify it was created
+ Assert.True(File.Exists(filePath));
+
+ // Remove the file
+ if (File.Exists(filePath))
+ {
+ File.Delete(filePath);
+ }
+ }
+
+ private string CreateDacpac(SqlTestDb testdb)
+ {
+ var result = GetLiveAutoCompleteTestObjects();
+ string folderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DacFxTest");
+ Directory.CreateDirectory(folderPath);
+
+ var extractParams = new ExtractParams
+ {
+ DatabaseName = testdb.DatabaseName,
+ PackageFilePath = Path.Combine(folderPath, string.Format("{0}.dacpac", testdb.DatabaseName)),
+ ApplicationName = "test",
+ ApplicationVersion = new Version(1, 0)
+ };
+
+ DacFxService service = new DacFxService();
+ ExtractOperation operation = new ExtractOperation(extractParams, result.ConnectionInfo);
+ service.PerformOperation(operation);
+
+ return extractParams.PackageFilePath;
+ }
+ }
+}