Skip to content

Commit ddacacd

Browse files
authored
Support for package references representing external databases (#53)
* Add support for references to external databases * Add SDK support for external references * Add test project with external reference
1 parent 553b8eb commit ddacacd

File tree

10 files changed

+93
-19
lines changed

10 files changed

+93
-19
lines changed

src/DacpacTool/BuildOptions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class BuildOptions
1010
public FileInfo Output { get; set; }
1111
public SqlServerVersion SqlServerVersion { get; set; }
1212
public FileInfo[] Input { get; set; }
13-
public FileInfo[] Reference { get; set; }
13+
public string[] Reference { get; set; }
1414
public string[] Property { get; set; }
1515
public string[] SqlCmdVar { get; set; }
1616
public FileInfo PreDeploy { get; set; }

src/DacpacTool/DacpacTool.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
<ItemGroup>
1010
<PackageReference Include="Microsoft.SqlServer.DACFX" Version="150.4826.1" />
11-
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20371.2" />
11+
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20427.1" />
1212
<PackageReference Include="System.Security.Permissions" Version="4.7.0" />
1313
</ItemGroup>
1414

src/DacpacTool/Extensions.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
using System.IO;
33
using System.Reflection;
44
using Microsoft.SqlServer.Dac.Model;
5+
using Microsoft.SqlServer.TransactSql.ScriptDom;
56

67
namespace MSBuild.Sdk.SqlProj.DacpacTool
78
{
89
public static class Extensions
910
{
10-
public static void AddReference(this TSqlModel model, string referencePath)
11+
public static void AddReference(this TSqlModel model, string referencePath, string externalParts)
1112
{
1213
var dataSchemaModel = GetDataSchemaModel(model);
1314

@@ -17,10 +18,14 @@ public static void AddReference(this TSqlModel model, string referencePath)
1718
setMetadataMethod.Invoke(customData, new object[] { "LogicalName", Path.GetFileName(referencePath) });
1819
setMetadataMethod.Invoke(customData, new object[] { "SuppressMissingDependenciesErrors", "False" });
1920

21+
if (!string.IsNullOrWhiteSpace(externalParts))
22+
{
23+
setMetadataMethod.Invoke(customData, new object[] { "ExternalParts", Identifier.EncodeIdentifier(externalParts) });
24+
}
25+
2026
AddCustomData(dataSchemaModel, customData);
2127
}
2228

23-
2429
public static void AddSqlCmdVariables(this TSqlModel model, string[] variableNames)
2530
{
2631
var dataSchemaModel = GetDataSchemaModel(model);

src/DacpacTool/PackageBuilder.cs

+24-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
24
using System.IO;
5+
using System.Linq;
36
using System.Reflection;
47
using System.Text;
58
using Microsoft.Data.Tools.Schema.Sql.Packaging;
@@ -18,19 +21,34 @@ public void UsingVersion(SqlServerVersion version)
1821
Console.WriteLine($"Using SQL Server version {version}");
1922
}
2023

21-
public void AddReference(FileInfo referenceFile)
24+
public void AddReference(string referenceFile)
2225
{
2326
// Ensure that the model has been created
2427
EnsureModelCreated();
2528

2629
// Make sure the file exists
27-
if (!referenceFile.Exists)
30+
if (!File.Exists(referenceFile))
2831
{
2932
throw new ArgumentException($"Unable to find reference file {referenceFile}", nameof(referenceFile));
3033
}
3134

32-
Console.WriteLine($"Adding reference to {referenceFile.FullName}");
33-
Model.AddReference(referenceFile.FullName);
35+
Console.WriteLine($"Adding reference to {referenceFile}");
36+
Model.AddReference(referenceFile, null);
37+
}
38+
39+
public void AddExternalReference(string referenceFile, string externalParts)
40+
{
41+
// Ensure that the model has been created
42+
EnsureModelCreated();
43+
44+
// Make sure the file exists
45+
if (!File.Exists(referenceFile))
46+
{
47+
throw new ArgumentException($"Unable to find reference file {referenceFile}", nameof(referenceFile));
48+
}
49+
50+
Console.WriteLine($"Adding reference to {referenceFile} with external parts {externalParts}");
51+
Model.AddReference(referenceFile, externalParts);
3452
}
3553

3654
public void AddSqlCmdVariables(string[] variableNames)
@@ -78,8 +96,8 @@ public bool ValidateModel()
7896

7997
// Validate the model and write out validation messages
8098
int validationErrors = 0;
81-
var messages = Model.Validate();
82-
foreach (var message in messages)
99+
var validationMessages = Model.Validate();
100+
foreach (var message in validationMessages)
83101
{
84102
if (message.MessageType == DacMessageType.Error)
85103
{

src/DacpacTool/Program.cs

+11-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ static async Task<int> Main(string[] args)
1919
new Option<FileInfo>(new string[] { "--output", "-o" }, "Filename of the output package"),
2020
new Option<SqlServerVersion>(new string[] { "--sqlServerVersion", "-sv" }, () => SqlServerVersion.Sql150, description: "Target version of the model"),
2121
new Option<FileInfo[]>(new string[] { "--input", "-i" }, "Input file name(s)"),
22-
new Option<FileInfo[]>(new string[] { "--reference", "-r" }, "Reference(s) to include"),
22+
new Option<string[]>(new string[] { "--reference", "-r" }, "Reference(s) to include"),
2323
new Option<FileInfo>(new string[] { "--predeploy" }, "Filename of optional pre-deployment script"),
2424
new Option<FileInfo>(new string[] { "--postdeploy" }, "Filename of optional post-deployment script"),
2525
new Option<FileInfo>(new string[] { "--refactorlog" }, "Filename of optional refactor log script"),
@@ -68,9 +68,17 @@ private static int BuildDacpac(BuildOptions options)
6868
// Add references to the model
6969
if (options.Reference != null)
7070
{
71-
foreach (var referenceFile in options.Reference)
71+
foreach (var reference in options.Reference)
7272
{
73-
packageBuilder.AddReference(referenceFile);
73+
string[] referenceDetails = reference.Split(';', 2, StringSplitOptions.RemoveEmptyEntries);
74+
if (referenceDetails.Length == 1)
75+
{
76+
packageBuilder.AddReference(referenceDetails[0]);
77+
}
78+
else
79+
{
80+
packageBuilder.AddExternalReference(referenceDetails[0], referenceDetails[1]);
81+
}
7482
}
7583
}
7684

src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets

+2-1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
<PhysicalLocation Condition="'%(_ResolvedPackageReference.PhysicalLocation)'==''">$([System.String]::new('$(NuGetPackageRoot)%(PackageReference.Identity)/%(PackageReference.Version)').ToLower())</PhysicalLocation>
125125

126126
<DacpacFile>%(_ResolvedPackageReference.PhysicalLocation)/tools/%(Identity).dacpac</DacpacFile>
127+
<DatabaseVariableLiteralValue>%(PackageReference.DatabaseVariableLiteralValue)</DatabaseVariableLiteralValue>
127128
</_ResolvedPackageReference>
128129

129130
<!-- Build a list of package references that include a dacpac file matching the package identity in their tools folder -->
@@ -150,7 +151,7 @@
150151
<OutputPathArgument>@(IntermediateAssembly->'-o &quot;%(Identity)&quot;', ' ')</OutputPathArgument>
151152
<MetadataArguments>-n &quot;$(MSBuildProjectName)&quot; -v &quot;$(PackageVersion)&quot;</MetadataArguments>
152153
<SqlServerVersionArgument>-sv $(SqlServerVersion)</SqlServerVersionArgument>
153-
<ReferenceArguments>@(DacpacReference->'-r &quot;%(DacpacFile)&quot;', ' ')</ReferenceArguments>
154+
<ReferenceArguments>@(DacpacReference->'-r &quot;%(DacpacFile);%(DatabaseVariableLiteralValue)&quot;', ' ')</ReferenceArguments>
154155
<InputFileArguments>@(Content->'-i &quot;%(FullPath)&quot;', ' ')</InputFileArguments>
155156
<PropertyArguments>@(PropertyNames->'-p %(Identity)=%(PropertyValue)', ' ')</PropertyArguments>
156157
<SqlCmdVariableArguments>@(SqlCmdVariable->'-sc %(Identity)', ' ')</SqlCmdVariableArguments>

test/DacpacTool.Tests/PackageBuilderTests.cs

+26-4
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,16 @@ public void AddReference_FileDoesNotExist()
6060
packageBuilder.UsingVersion(SqlServerVersion.Sql150);
6161

6262
// Act & Assert
63-
Should.Throw<ArgumentException>(() => packageBuilder.AddReference(new FileInfo("NonExistentFile.dacpac")));
63+
Should.Throw<ArgumentException>(() => packageBuilder.AddReference("NonExistentFile.dacpac"));
6464
}
6565

6666
[TestMethod]
6767
public void AddReference_FileExists()
6868
{
6969
// Arrange
70-
var reference = new FileInfo(new TestModelBuilder()
70+
var reference = new TestModelBuilder()
7171
.AddStoredProcedure("MyStoredProcedure", "SELECT 1;")
72-
.SaveAsPackage());
72+
.SaveAsPackage();
7373
var packageBuilder = new PackageBuilder();
7474
packageBuilder.UsingVersion(SqlServerVersion.Sql150);
7575

@@ -80,7 +80,29 @@ public void AddReference_FileExists()
8080
packageBuilder.Model.GetObject(Procedure.TypeClass, new ObjectIdentifier("dbo", "MyStoredProcedure"), DacQueryScopes.All).ShouldNotBeNull();
8181

8282
// Cleanup
83-
reference.Delete();
83+
File.Delete(reference);
84+
}
85+
86+
[TestMethod]
87+
public void AddReference_DifferentDatabase()
88+
{
89+
// Arrange
90+
var reference = new TestModelBuilder()
91+
.AddStoredProcedure("MyStoredProcedure", "SELECT 1;")
92+
.SaveAsPackage();
93+
var packageBuilder = new PackageBuilder();
94+
packageBuilder.UsingVersion(SqlServerVersion.Sql150);
95+
96+
// Act
97+
packageBuilder.AddExternalReference(reference, "SomeOtherDatabase");
98+
99+
// Assert
100+
var referencingStoredProcedure = "CREATE PROCEDURE [MyOtherStoredProcedure] AS BEGIN EXEC [SomeOtherDatabase].[dbo].[MyStoredProcedure] END";
101+
packageBuilder.Model.AddObjects(referencingStoredProcedure);
102+
packageBuilder.Model.Validate().Any().ShouldBeFalse();
103+
104+
// Cleanup
105+
File.Delete(reference);
84106
}
85107

86108
[TestMethod]

test/DacpacTool.Tests/TestModelBuilder.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public TestModelBuilder AddStoredProcedure(string procName, string body)
3737

3838
public TestModelBuilder AddReference(string path)
3939
{
40-
sqlModel.AddReference(path);
40+
sqlModel.AddReference(path, string.Empty);
4141
return this;
4242
}
4343

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project>
2+
<Import Project="$(MSBuildThisFileDirectory)../../src/MSBuild.Sdk.SqlProj/Sdk/Sdk.props" />
3+
4+
<PropertyGroup>
5+
<TargetFramework>netstandard2.0</TargetFramework>
6+
<SqlServerVersion>Sql110</SqlServerVersion>
7+
<RestoreAdditionalProjectSources>../TestProject/bin/Debug</RestoreAdditionalProjectSources>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="TestProject" Version="1.2.0-beta.9.gf2d69b1c16" DatabaseVariableLiteralValue="SomeOtherDatabase" />
12+
</ItemGroup>
13+
14+
<Import Project="$(MSBuildThisFileDirectory)../../src/MSBuild.Sdk.SqlProj/Sdk/Sdk.targets" />
15+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE PROCEDURE [dbo].[csp_Test]
2+
AS
3+
BEGIN
4+
SELECT * FROM [SomeOtherDatabase].[dbo].[MyTable];
5+
END

0 commit comments

Comments
 (0)