Skip to content

Commit 910da9e

Browse files
javiercnCopilot
andcommitted
Address remaining PR feedback and fix Docker Compose publish
PR feedback from #15691: - Add [ResourceName] attribute to all Add* API name parameters - Replace null-forgiving operator with explicit throw in EndpointsManifestTransformer - Use StringComparison.OrdinalIgnoreCase for route comparison - Fix '.NET Aspire' phrasing in README files Docker Compose publish fixes for Blazor gateway: - Add GatewayOriginReference (IValueProvider + IManifestExpressionProvider) - Use ReferenceExpression for ConfigResponse so publishers emit proper placeholders - Use TextEncoderSettings to escape braces in JSON string values - Simplify Gateway.cs.in (serve ConfigResponse directly, no Replace logic) - Add System.Text.Unicode dependency for brace-escaping encoder Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a4c4d9e commit 910da9e

9 files changed

Lines changed: 166 additions & 149 deletions

File tree

playground/BlazorHosted/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# BlazorHosted
22

3-
This sample demonstrates how to integrate a **hosted Blazor WebAssembly** application (Blazor Web App with Interactive WebAssembly render mode) with .NET Aspire, enabling full observability (logs, traces) and service discovery for both the server and the WASM client.
3+
This sample demonstrates how to integrate a **hosted Blazor WebAssembly** application (Blazor Web App with Interactive WebAssembly render mode) with Aspire, enabling full observability (logs, traces) and service discovery for both the server and the WASM client.
44

55
## Overview
66

playground/BlazorStandalone/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# BlazorStandalone
22

3-
This sample demonstrates how to integrate a **standalone Blazor WebAssembly** application with .NET Aspire, enabling full observability (logs, traces) and service discovery without requiring a hosted Blazor Server backend.
3+
This sample demonstrates how to integrate a **standalone Blazor WebAssembly** application with Aspire, enabling full observability (logs, traces) and service discovery without requiring a hosted Blazor Server backend.
44

55
## Overview
66

src/Aspire.Hosting.Blazor/Aspire.Hosting.Blazor.csproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,20 @@
1111
<PropertyGroup>
1212
<MinCodeCoverage>0</MinCodeCoverage>
1313
<EnablePackageValidation>false</EnablePackageValidation>
14+
<!-- The minimum .NET version for Docker images used in the Blazor gateway publish pipeline.
15+
Derived from the highest TFM in the repo (currently net10.0). The Gateway.cs file-based
16+
app and the WASM client projects target this version, so published containers must use
17+
matching SDK/runtime base images. -->
18+
<BlazorGatewayDotNetImageTag>10.0</BlazorGatewayDotNetImageTag>
1419
</PropertyGroup>
1520

21+
<ItemGroup>
22+
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute">
23+
<_Parameter1>BlazorGatewayDotNetImageTag</_Parameter1>
24+
<_Parameter2>$(BlazorGatewayDotNetImageTag)</_Parameter2>
25+
</AssemblyAttribute>
26+
</ItemGroup>
27+
1628
<ItemGroup>
1729
<ProjectReference Include="..\Aspire.Hosting\Aspire.Hosting.csproj" />
1830
</ItemGroup>

src/Aspire.Hosting.Blazor/BlazorGatewayExtensions.cs

Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@ namespace Aspire.Hosting;
2121
[Experimental("ASPIREBLAZOR001", UrlFormat = "https://aka.ms/aspire/diagnostics/{0}")]
2222
public static class BlazorGatewayExtensions
2323
{
24-
// Derive the .NET image tag from the runtime version of the app host process.
25-
// The Gateway is a file-based app compiled with the same SDK, so the major.minor
26-
// version of the running host matches the required SDK/ASP.NET base images.
27-
// Pre-release runtimes (preview/RC) use suffixed tags like "10.0-preview" or "11.0-rc".
2824
private static readonly string s_dotNetImageTag = GetDotNetImageTag();
2925
private const string DotNetSdkImageRepo = "mcr.microsoft.com/dotnet/sdk";
3026
private const string DotNetAspNetImageRepo = "mcr.microsoft.com/dotnet/aspnet";
@@ -37,7 +33,7 @@ public static class BlazorGatewayExtensions
3733
[AspireExportIgnore(Reason = "Blazor gateway APIs are not yet stable for ATS export.")]
3834
public static IResourceBuilder<ProjectResource> AddBlazorGateway(
3935
this IDistributedApplicationBuilder builder,
40-
string name)
36+
[ResourceName] string name)
4137
{
4238
var gatewayPath = GetScriptPath("Gateway.cs");
4339
var gateway = builder.AddCSharpApp(name, gatewayPath)
@@ -84,7 +80,7 @@ public static IResourceBuilder<ProjectResource> AddBlazorGateway(
8480
[AspireExportIgnore(Reason = "Open generic type parameter TProject is not ATS-compatible.")]
8581
public static IResourceBuilder<BlazorWasmAppResource> AddBlazorWasmProject<TProject>(
8682
this IDistributedApplicationBuilder builder,
87-
string name)
83+
[ResourceName] string name)
8884
where TProject : IProjectMetadata, new()
8985
{
9086
var metadata = new TProject();
@@ -109,7 +105,7 @@ public static IResourceBuilder<BlazorWasmAppResource> AddBlazorWasmProject<TProj
109105
[AspireExportIgnore(Reason = "Blazor gateway APIs are not yet stable for ATS export.")]
110106
public static IResourceBuilder<BlazorWasmAppResource> AddBlazorWasmApp(
111107
this IDistributedApplicationBuilder builder,
112-
string name,
108+
[ResourceName] string name,
113109
string projectPath)
114110
{
115111
var resolvedPath = Path.GetFullPath(Path.Combine(builder.AppHostDirectory, projectPath));
@@ -201,8 +197,7 @@ internal static IResourceBuilder<ProjectResource> WithBlazorApp(
201197
var annotation = GetOrAddGatewayAppsAnnotation(gateway.Resource);
202198

203199
var gatewayOutputRoot = Path.Combine(
204-
gateway.ApplicationBuilder.AppHostDirectory,
205-
"obj", "Aspire.Hosting.Blazor", "gateways", gateway.Resource.Name);
200+
GetBlazorStorePath(gateway.ApplicationBuilder), "gateways", gateway.Resource.Name);
206201

207202
if (!annotation.IsInitialized)
208203
{
@@ -253,9 +248,8 @@ internal static IResourceBuilder<ProjectResource> WithBlazorApp(
253248
// and isolated mode). Fall back to configuration for cases where the dashboard
254249
// resource isn't in the model (e.g. external dashboard).
255250
var httpOtlpEndpointUrl = ResolveHttpOtlpEndpointUrl(context, gateway.ApplicationBuilder.Configuration);
256-
var resourceLoggerService = context.ExecutionContext.ServiceProvider.GetRequiredService<ResourceLoggerService>();
257251

258-
GatewayConfigurationBuilder.EmitProxyConfiguration(context.EnvironmentVariables, registeredApps, gatewayEndpoint, httpGatewayEndpoint, httpOtlpEndpointUrl, resourceLoggerService);
252+
GatewayConfigurationBuilder.EmitProxyConfiguration(context.EnvironmentVariables, registeredApps, gatewayEndpoint, httpGatewayEndpoint, httpOtlpEndpointUrl);
259253
});
260254
}
261255

@@ -413,14 +407,14 @@ private static void CreatePublishCompanion(
413407
var relativeProjectPath = Path.GetRelativePath(
414408
project.SolutionRoot, wasmApp.Resource.ProjectPath).Replace('\\', '/');
415409

416-
// Copy the PrefixEndpoints.cs script into a project-local build folder so it's
417-
// available inside the Docker build context without clobbering the solution root.
410+
// Copy PrefixEndpoints.cs into .aspire/scripts/ within the solution root so it's
411+
// included in the Docker build context.
418412
var scriptSource = GetScriptPath("PrefixEndpoints.cs");
419-
var scriptRelativePath = Path.Combine(project.RelativeProjectPath, "obj", "Aspire.Hosting.Blazor", "PrefixEndpoints.cs")
420-
.Replace('\\', '/');
421-
var scriptDest = Path.Combine(project.SolutionRoot, scriptRelativePath.Replace('/', Path.DirectorySeparatorChar));
413+
var scriptDest = Path.Combine(project.SolutionRoot, ".aspire", "scripts", "PrefixEndpoints.cs");
422414
Directory.CreateDirectory(Path.GetDirectoryName(scriptDest)!);
423415
File.Copy(scriptSource, scriptDest, overwrite: true);
416+
var scriptRelativePath = Path.GetRelativePath(project.SolutionRoot, scriptDest)
417+
.Replace('\\', '/');
424418

425419
var companion = gateway.ApplicationBuilder.AddResource(
426420
new BlazorWasmPublishResource(publishResourceName))
@@ -466,6 +460,17 @@ private static string GetScriptPath(string scriptName)
466460
return scriptPath;
467461
}
468462

463+
/// <summary>
464+
/// Gets the Blazor-specific store path under the Aspire store directory.
465+
/// </summary>
466+
private static string GetBlazorStorePath(IDistributedApplicationBuilder builder)
467+
{
468+
var storePath = builder.Configuration["Aspire:Store:Path"]
469+
?? builder.AppHostDirectory;
470+
471+
return Path.Combine(storePath, ".aspire", "blazor");
472+
}
473+
469474
private static List<EndpointReferenceAnnotation> GetServiceDiscoveryReferences(IResource resource)
470475
{
471476
// EndpointReferenceAnnotation is added by WithReference and tracks which endpoint
@@ -637,30 +642,58 @@ private readonly struct ProjectInfo(string solutionRoot, string relativeProjectP
637642
}
638643

639644
/// <summary>
640-
/// Resolves the Docker image tag for the .NET SDK/ASP.NET base images.
641-
/// Returns "Major.Minor" for stable releases (e.g. "10.0", "11.0"),
642-
/// and appends "-preview" or "-rc" for pre-release runtimes to match the
643-
/// MCR tag naming convention (e.g. "10.0-preview", "11.0-rc").
645+
/// Resolves the Docker image tag for .NET base images. Uses the maximum of the build-time
646+
/// stamped version and the actual runtime version, with pre-release suffix when applicable.
644647
/// </summary>
645648
private static string GetDotNetImageTag()
646649
{
647-
var tag = $"{Environment.Version.Major}.{Environment.Version.Minor}";
650+
var runtimeMajor = Environment.Version.Major;
651+
var runtimeMinor = Environment.Version.Minor;
648652

649-
// The runtime's informational version contains the full pre-release label,
650-
// e.g. "10.0.0-preview.7.25352.1+..." or "11.0.0-rc.1.25400.3+...".
651-
// Stable/servicing builds use "10.0.6-servicing..." which we ignore.
652-
var informationalVersion = (System.Reflection.AssemblyInformationalVersionAttribute?)
653-
Attribute.GetCustomAttribute(typeof(object).Assembly, typeof(System.Reflection.AssemblyInformationalVersionAttribute));
653+
var stampedMajor = runtimeMajor;
654+
var stampedMinor = runtimeMinor;
654655

655-
if (informationalVersion is not null)
656+
var stampedValue = typeof(BlazorGatewayExtensions).Assembly
657+
.GetCustomAttributes(typeof(System.Reflection.AssemblyMetadataAttribute), inherit: false)
658+
.OfType<System.Reflection.AssemblyMetadataAttribute>()
659+
.FirstOrDefault(a => a.Key == "BlazorGatewayDotNetImageTag")
660+
?.Value;
661+
662+
if (!string.IsNullOrEmpty(stampedValue))
656663
{
657-
if (informationalVersion.InformationalVersion.Contains("-preview", StringComparison.OrdinalIgnoreCase))
664+
var parts = stampedValue.Split('.');
665+
if (parts.Length >= 2
666+
&& int.TryParse(parts[0], out var sMajor)
667+
&& int.TryParse(parts[1], out var sMinor))
658668
{
659-
tag += "-preview";
669+
stampedMajor = sMajor;
670+
stampedMinor = sMinor;
660671
}
661-
else if (informationalVersion.InformationalVersion.Contains("-rc", StringComparison.OrdinalIgnoreCase))
672+
}
673+
674+
var major = Math.Max(runtimeMajor, stampedMajor);
675+
var minor = (major == runtimeMajor && major == stampedMajor)
676+
? Math.Max(runtimeMinor, stampedMinor)
677+
: (major == runtimeMajor ? runtimeMinor : stampedMinor);
678+
679+
var tag = $"{major}.{minor}";
680+
681+
// Append pre-release suffix when the runtime version won and is pre-release.
682+
if (major == runtimeMajor && minor == runtimeMinor)
683+
{
684+
var informationalVersion = (System.Reflection.AssemblyInformationalVersionAttribute?)
685+
Attribute.GetCustomAttribute(typeof(object).Assembly, typeof(System.Reflection.AssemblyInformationalVersionAttribute));
686+
687+
if (informationalVersion is not null)
662688
{
663-
tag += "-rc";
689+
if (informationalVersion.InformationalVersion.Contains("-preview", StringComparison.OrdinalIgnoreCase))
690+
{
691+
tag += "-preview";
692+
}
693+
else if (informationalVersion.InformationalVersion.Contains("-rc", StringComparison.OrdinalIgnoreCase))
694+
{
695+
tag += "-rc";
696+
}
664697
}
665698
}
666699

src/Aspire.Hosting.Blazor/BlazorHostedExtensions.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ private static void EnsureEnvironmentCallback(
9999
annotation.Services,
100100
annotation.ProxyBlazorTelemetry,
101101
httpOtlpEndpointUrl,
102-
context.Logger,
103102
annotation.OtlpPrefix);
104103
});
105104
}

0 commit comments

Comments
 (0)