Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
var db2 = sql2.AddDatabase("db2");

var api = builder.AddProject<Projects.SqlServerEndToEnd_ApiService>("api")
.WithReference(db1)
.WithReference(db2)
.WithExternalHttpEndpoints();

// Add EF migrations resource for the api project
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#nullable disable

namespace SqlServerEndToEnd.Common.Db1;
namespace SqlServerEndToEnd.Common.Db1Migrations;

/// <inheritdoc />
public partial class Initial : Migration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#nullable disable

namespace SqlServerEndToEnd.Common.Db1
namespace SqlServerEndToEnd.Common.Db1Migrations
Comment thread
AndriySvyryd marked this conversation as resolved.
Resolved
{
[DbContext(typeof(MyDb1Context))]
partial class MyDb1ContextModelSnapshot : ModelSnapshot
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#nullable disable

namespace SqlServerEndToEnd.Common.Db2;
namespace SqlServerEndToEnd.Common.Db2Migrations;

/// <inheritdoc />
public partial class Initial : Migration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

#nullable disable

namespace SqlServerEndToEnd.Common.Db2
namespace SqlServerEndToEnd.Common.Db2Migrations
Comment thread
AndriySvyryd marked this conversation as resolved.
Resolved
{
[DbContext(typeof(MyDb2Context))]
partial class MyDb2ContextModelSnapshot : ModelSnapshot
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);IDE0161;IDE0005</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,23 @@ private static void AddEFMigrationCommands(
}
}

// Forward the migration resource's own environment to the tool resource at start time.
// The connection string the user declares via `.WithReference(<db>)` lands on the migration
// resource as an EnvironmentCallbackAnnotation *after* this method runs, so the annotations
// cannot be copied eagerly like the project ones above. Evaluating them lazily here ensures the
// dotnet-ef process receives `ConnectionStrings__<db>`; without it the design-time DbContext
// has no connection string and fails with "The ConnectionString property has not been initialized."
toolBuilder.WithEnvironment(async context =>
{
if (migrationResource.TryGetAnnotationsOfType<EnvironmentCallbackAnnotation>(out var migrationEnvCallbacks))
{
foreach (var callback in migrationEnvCallbacks)
{
await callback.Callback(context).ConfigureAwait(false);
}
}
});

migrationResource.ToolResource = toolBuilder.Resource;

migrationBuilder.WithCommand(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#pragma warning disable ASPIREDOTNETTOOL

using Aspire.Hosting.ApplicationModel;
using Aspire.Hosting.Utils;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -225,7 +227,48 @@ public void EFMigrationResourceHasOptionsProperty()
Assert.False(migrations.Resource.PublishAsMigrationBundle);
}

[Fact]
public async Task ToolResourceInheritsConnectionStringFromMigrationReference()
{
// The connection string the user declares via .WithReference(<db>) lands on the migration
// resource, not on the startup project. The dotnet-ef tool resource must inherit it so the
// design-time DbContext can open the database. Regression test for the
// "The ConnectionString property has not been initialized." failure.
//
// The forwarding under test is operation-mode agnostic, so this asserts in Publish mode where
// references resolve to manifest expressions synchronously. Run mode is intentionally avoided
// because the tool resource also eagerly inherits the startup project's endpoint references,
// and resolving those in Run mode awaits runtime endpoint allocation (a TaskCompletionSource
// only completed by DCP when the app is actually running) — which deadlocks a plain unit test.
using var builder = TestDistributedApplicationBuilder.Create(DistributedApplicationOperation.Publish);
var db = builder.AddResource(new TestDatabaseResource("db1"));
var project = builder.AddProject<Projects.ServiceA>("myproject");
project.AddEFMigrations("mymigrations", typeof(TestDbContext).FullName!)
.WithReference(db);

using var app = builder.Build();
var appModel = app.Services.GetRequiredService<DistributedApplicationModel>();

var toolResource = Assert.Single(appModel.Resources.OfType<DotnetToolResource>(), r => r.Name == "ef-tool-mymigrations");

#pragma warning disable CS0618 // Type or member is obsolete
var env = await toolResource.GetEnvironmentVariableValuesAsync(DistributedApplicationOperation.Publish);
#pragma warning restore CS0618 // Type or member is obsolete

Assert.Equal("{db1.connectionString}", Assert.Contains("ConnectionStrings__db1", env));
}

// Test classes for DbContext types
private sealed class TestDbContext { }
private sealed class AnotherDbContext { }

/// <summary>
/// A minimal test resource that implements IResourceWithConnectionString so it can be used as the
/// target of <c>WithReference</c> on an EF migration resource.
/// </summary>
private sealed class TestDatabaseResource(string name) : Resource(name), IResourceWithConnectionString
{
public ReferenceExpression ConnectionStringExpression =>
ReferenceExpression.Create($"Host=localhost;Database={Name}");
}
Comment thread
AndriySvyryd marked this conversation as resolved.
Resolved
}
Loading