//
// 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.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.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Linq;
namespace Microsoft.SqlTools.ServiceLayer.SchemaCompare
{
///
/// Schema compare load scmp operation
///
class SchemaCompareOpenScmpOperation : ITaskOperation
{
private CancellationTokenSource cancellation = new CancellationTokenSource();
private bool disposed = false;
public SqlTask SqlTask { get; set; }
public SchemaCompareOpenScmpParams Parameters { get; set; }
public SchemaCompareOpenScmpResult Result { get; private set; }
private XDocument scmpInfo { get; set; }
public SchemaCompareOpenScmpOperation(SchemaCompareOpenScmpParams parameters)
{
Validate.IsNotNull("parameters", parameters);
this.Parameters = parameters;
}
protected CancellationToken CancellationToken { get { return this.cancellation.Token; } }
///
/// The error occurred during operation
///
public string ErrorMessage { get; set; }
// The schema compare public api doesn't currently take a cancellation token so the operation can't be cancelled
public void 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
{
SchemaComparison compare = new SchemaComparison(this.Parameters.FilePath);
// load xml file because some parsing still needs to be done
this.scmpInfo = XDocument.Load(this.Parameters.FilePath);
this.Result = new SchemaCompareOpenScmpResult()
{
DeploymentOptions = new DeploymentOptions(compare.Options),
Success = true,
SourceEndpointInfo = this.GetEndpointInfo(true, compare.Source),
TargetEndpointInfo = this.GetEndpointInfo(false, compare.Target),
OriginalTargetName = this.GetOriginalTargetName(),
OriginalTargetConnectionString = this.GetOriginalTargetConnectionString(),
ExcludedSourceElements = this.GetExcludedElements(compare.ExcludedSourceObjects),
ExcludedTargetElements = this.GetExcludedElements(compare.ExcludedTargetObjects)
};
}
catch (Exception e)
{
ErrorMessage = e.Message;
Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed with exception {0}", e));
throw;
}
}
private SchemaCompareEndpointInfo GetEndpointInfo(bool source, SchemaCompareEndpoint endpoint)
{
SchemaCompareEndpointInfo endpointInfo = new SchemaCompareEndpointInfo();
// if the endpoint is a dacpac we don't need to parse the xml
SchemaCompareDacpacEndpoint dacpacEndpoint = endpoint as SchemaCompareDacpacEndpoint;
if (dacpacEndpoint != null)
{
endpointInfo.EndpointType = SchemaCompareEndpointType.Dacpac;
endpointInfo.PackageFilePath = dacpacEndpoint.FilePath;
}
else
{
// need to parse xml to get connection string of database
var result = this.scmpInfo.Descendants("ConnectionBasedModelProvider");
string searchingFor = source ? "Source" : "Target";
try
{
if (result != null)
{
foreach (XElement node in result)
{
if (node.Parent.Name.ToString().Contains(searchingFor))
{
endpointInfo.ConnectionDetails = SchemaCompareService.ConnectionServiceInstance.ParseConnectionString(node.Value);
endpointInfo.ConnectionDetails.ConnectionString = node.Value;
endpointInfo.DatabaseName = endpointInfo.ConnectionDetails.DatabaseName;
endpointInfo.EndpointType = SchemaCompareEndpointType.Database;
}
}
}
}
catch (Exception e)
{
ErrorMessage = string.Format(SR.OpenScmpConnectionBasedModelParsingError, ((SchemaCompareDatabaseEndpoint)endpoint).DatabaseName,e.Message);
Logger.Write(TraceEventType.Error, string.Format("Schema compare open scmp operation failed during xml parsing with exception {0}", e.Message));
throw;
}
}
return endpointInfo;
}
private List GetExcludedElements(IList excludedObjects)
{
List excludedElements = new List();
foreach (SchemaComparisonExcludedObjectId entry in excludedObjects)
{
excludedElements.Add(new SchemaCompareObjectId()
{
NameParts = entry.Identifier.Parts.Cast().ToArray(),
SqlObjectType = entry.TypeName
});
}
return excludedElements;
}
// The original target name is used to determine whether to use ExcludedSourceElements or ExcludedTargetElements if source and target were swapped
private string GetOriginalTargetName()
{
var result = this.scmpInfo.Descendants("PropertyElementName")
.Where(x => x.Element("Name").Value == "TargetDatabaseName")
.Select(x => x.Element("Value")).FirstOrDefault();
return result != null ? result.Value : string.Empty;
}
// The original target connection string is used if comparing a dacpac and db with the same name
private string GetOriginalTargetConnectionString()
{
var result = this.scmpInfo.Descendants("PropertyElementName")
.Where(x => x.Element("Name").Value == "TargetConnectionString")
.Select(x => x.Element("Value")).FirstOrDefault();
return result != null ? result.Value : string.Empty;
}
}
}