Skip to content

Commit 9072de4

Browse files
authored
Merge pull request #729 from ErikEJ/issue-694
Add support for DacDeployOption from a publish profile
1 parent 58b22e3 commit 9072de4

File tree

9 files changed

+163
-1
lines changed

9 files changed

+163
-1
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<IncludeCompositeObjects>False</IncludeCompositeObjects>
5+
<TargetDatabaseName>Database</TargetDatabaseName>
6+
<DeployScriptFileName>Database.sql</DeployScriptFileName>
7+
<BlockOnPossibleDataLoss>True</BlockOnPossibleDataLoss>
8+
<ProfileVersionNumber>1</ProfileVersionNumber>
9+
</PropertyGroup>
10+
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Aspire.Hosting.ApplicationModel;
2+
3+
/// <summary>
4+
/// Represents a metadata annotation that specifies dacpac deployment options.
5+
/// </summary>
6+
/// <param name="OptionsPath">path to deployment options xml file</param>
7+
public record DacDeployOptionsAnnotation(string OptionsPath) : IResourceAnnotation
8+
{
9+
}

src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/SqlPackageResource.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,19 @@ DacDeployOptions IResourceWithDacpac.GetDacpacDeployOptions()
3737
{
3838
var options = new DacDeployOptions();
3939

40+
if (this.TryGetLastAnnotation<DacDeployOptionsAnnotation>(out var optionsAnnotation))
41+
{
42+
var profile = DacProfile.Load(optionsAnnotation.OptionsPath);
43+
44+
if (profile == null)
45+
{
46+
throw new InvalidOperationException($"Unable to load DacProfile from path {optionsAnnotation.OptionsPath} for resource {Name}.");
47+
}
48+
49+
options = profile.DeployOptions;
50+
return options;
51+
}
52+
4053
if (this.TryGetLastAnnotation<ConfigureDacDeployOptionsAnnotation>(out var configureAnnotation))
4154
{
4255
configureAnnotation.ConfigureDeploymentOptions(options);

src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/SqlProjectBuilderExtensions.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,34 @@ internal static IResourceBuilder<TResource> InternalWithConfigureDacDeployOption
142142
.WithAnnotation(new ConfigureDacDeployOptionsAnnotation(configureDeploymentOptions));
143143
}
144144

145+
/// <summary>
146+
/// Adds a path to a publish profile for configuring dacpac deployment options to the <see cref="SqlProjectResource"/>.
147+
/// </summary>
148+
/// <param name="builder">An <see cref="IResourceBuilder{T}"/> representing the SQL Server Database project.</param>
149+
/// <param name="optionsPath">Path to the publish profile xml file</param>
150+
/// <returns>An <see cref="IResourceBuilder{T}"/> that can be used to further customize the resource.</returns>
151+
public static IResourceBuilder<SqlProjectResource> WithDacDeployOptions(this IResourceBuilder<SqlProjectResource> builder, string optionsPath)
152+
=> InternalWithDacDeployOptions(builder, optionsPath);
153+
154+
/// <summary>
155+
/// Adds a path to a publish profile for configuring dacpac deployment options to the <see cref="SqlProjectResource"/>.
156+
/// </summary>
157+
/// <param name="builder">An <see cref="IResourceBuilder{T}"/> representing the SQL Server Database project.</param>
158+
/// <param name="optionsPath">Path to the publish profile xml file</param>
159+
/// <returns>An <see cref="IResourceBuilder{T}"/> that can be used to further customize the resource.</returns>
160+
public static IResourceBuilder<SqlPackageResource<TPackage>> WithDacDeployOptions<TPackage>(this IResourceBuilder<SqlPackageResource<TPackage>> builder, string optionsPath)
161+
where TPackage : IPackageMetadata => InternalWithDacDeployOptions(builder, optionsPath);
162+
163+
internal static IResourceBuilder<TResource> InternalWithDacDeployOptions<TResource>(this IResourceBuilder<TResource> builder, string optionsPath)
164+
where TResource : IResourceWithDacpac
165+
{
166+
ArgumentNullException.ThrowIfNull(builder, nameof(builder));
167+
ArgumentNullException.ThrowIfNull(optionsPath);
168+
169+
return builder
170+
.WithAnnotation(new DacDeployOptionsAnnotation(optionsPath));
171+
}
172+
145173
/// <summary>
146174
/// Publishes the SQL Server Database project to the target <see cref="SqlServerDatabaseResource"/>.
147175
/// </summary>

src/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects/SqlProjectResource.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ string IResourceWithDacpac.GetDacpacPath()
2323

2424
var project = projectCollection.LoadProject(projectPath);
2525

26-
// .sqlprojx has a SqlTargetPath property, so try that first
26+
// Microsoft.Build.Sql .sqlproj has a SqlTargetPath property, so try that first
2727
var targetPath = project.GetPropertyValue("SqlTargetPath");
2828
if (string.IsNullOrWhiteSpace(targetPath))
2929
{
@@ -45,6 +45,19 @@ DacDeployOptions IResourceWithDacpac.GetDacpacDeployOptions()
4545
{
4646
var options = new DacDeployOptions();
4747

48+
if (this.TryGetLastAnnotation<DacDeployOptionsAnnotation>(out var optionsAnnotation))
49+
{
50+
var profile = DacProfile.Load(optionsAnnotation.OptionsPath);
51+
52+
if (profile == null)
53+
{
54+
throw new InvalidOperationException($"Unable to load DacProfile from path {optionsAnnotation.OptionsPath} for resource {Name}.");
55+
}
56+
57+
options = profile.DeployOptions;
58+
return options;
59+
}
60+
4861
if (this.TryGetLastAnnotation<ConfigureDacDeployOptionsAnnotation>(out var configureAnnotation))
4962
{
5063
configureAnnotation.ConfigureDeploymentOptions(options);

tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/AddSqlPackageTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,4 +91,29 @@ public void AddSqlPackage_WithDeploymentOptions()
9191
var options = ((IResourceWithDacpac)sqlProjectResource).GetDacpacDeployOptions();
9292
Assert.True(options.IncludeCompositeObjects);
9393
}
94+
95+
[Fact]
96+
public void AddSqlPackage_WithDeploymentOptions_FromFile()
97+
{
98+
// Arrange
99+
var appBuilder = DistributedApplication.CreateBuilder();
100+
101+
var optionsPath = "Database.publish.xml";
102+
103+
appBuilder.AddSqlPackage<TestPackage>("chinook").WithDacDeployOptions(optionsPath);
104+
105+
// Act
106+
using var app = appBuilder.Build();
107+
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
108+
109+
// Assert
110+
var sqlProjectResource = Assert.Single(appModel.Resources.OfType<SqlPackageResource<TestPackage>>());
111+
Assert.Equal("chinook", sqlProjectResource.Name);
112+
113+
Assert.True(sqlProjectResource.TryGetLastAnnotation(out DacDeployOptionsAnnotation? dacDeployOptionsAnnotation));
114+
Assert.Equal(optionsPath, dacDeployOptionsAnnotation.OptionsPath);
115+
116+
var options = ((IResourceWithDacpac)sqlProjectResource).GetDacpacDeployOptions();
117+
Assert.False(options.BlockOnPossibleDataLoss);
118+
}
94119
}

tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/AddSqlProjectTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,55 @@ public void AddSqlProject_WithDeploymentOptions()
9494
Assert.True(options.IncludeCompositeObjects);
9595
}
9696

97+
[Fact]
98+
public void AddSqlProject_WithDeploymentOptions_FromFile_NonExisting()
99+
{
100+
// Arrange
101+
var appBuilder = DistributedApplication.CreateBuilder();
102+
103+
var optionsPath = "/folder/project.publish.xml";
104+
105+
appBuilder.AddSqlProject("MySqlProject").WithDacDeployOptions(optionsPath);
106+
107+
// Act
108+
using var app = appBuilder.Build();
109+
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
110+
111+
// Assert
112+
var sqlProjectResource = Assert.Single(appModel.Resources.OfType<SqlProjectResource>());
113+
Assert.Equal("MySqlProject", sqlProjectResource.Name);
114+
115+
Assert.True(sqlProjectResource.TryGetLastAnnotation(out DacDeployOptionsAnnotation? dacDeployOptionsAnnotation));
116+
Assert.Equal(optionsPath, dacDeployOptionsAnnotation.OptionsPath);
117+
118+
Assert.Throws<DirectoryNotFoundException>(() => ((IResourceWithDacpac)sqlProjectResource).GetDacpacDeployOptions());
119+
}
120+
121+
[Fact]
122+
public void AddSqlProject_WithDeploymentOptions_FromFile()
123+
{
124+
// Arrange
125+
var appBuilder = DistributedApplication.CreateBuilder();
126+
127+
var optionsPath = "Database.publish.xml";
128+
129+
appBuilder.AddSqlProject("MySqlProject").WithDacDeployOptions(optionsPath);
130+
131+
// Act
132+
using var app = appBuilder.Build();
133+
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();
134+
135+
// Assert
136+
var sqlProjectResource = Assert.Single(appModel.Resources.OfType<SqlProjectResource>());
137+
Assert.Equal("MySqlProject", sqlProjectResource.Name);
138+
139+
Assert.True(sqlProjectResource.TryGetLastAnnotation(out DacDeployOptionsAnnotation? dacDeployOptionsAnnotation));
140+
Assert.Equal(optionsPath, dacDeployOptionsAnnotation.OptionsPath);
141+
142+
var options = ((IResourceWithDacpac)sqlProjectResource).GetDacpacDeployOptions();
143+
Assert.False(options.BlockOnPossibleDataLoss);
144+
}
145+
97146
[Fact]
98147
public void WithReference_AddsRequiredServices()
99148
{

tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests/CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.Tests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,10 @@
1010
<ProjectReference Include="..\..\examples\sql-database-projects\CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost\CommunityToolkit.Aspire.Hosting.SqlDatabaseProjects.AppHost.csproj" />
1111
</ItemGroup>
1212

13+
<ItemGroup>
14+
<None Update="Database.publish.xml">
15+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
16+
</None>
17+
</ItemGroup>
18+
1319
</Project>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<TargetDatabaseName>Database</TargetDatabaseName>
5+
<DeployScriptFileName>Database.sql</DeployScriptFileName>
6+
<BlockOnPossibleDataLoss>False</BlockOnPossibleDataLoss>
7+
<ProfileVersionNumber>1</ProfileVersionNumber>
8+
</PropertyGroup>
9+
</Project>

0 commit comments

Comments
 (0)