Use Framework SourceType for WASM pass-through assets in multi-client solutions#125329
Use Framework SourceType for WASM pass-through assets in multi-client solutions#125329lewing merged 10 commits intodotnet:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes a crash introduced by PR #124125 in multi-client Blazor WebAssembly solutions. When two WASM client projects reference the same runtime pack, pass-through assets (JS, maps, ICU data, native WASM) previously resolved to identical NuGet cache paths, causing duplicate Identity keys that crashed DiscoverPrecompressedAssets with an ArgumentException during ToDictionary. The fix materializes pass-through files to a per-project obj/fx/{PackageId}/ directory using the SourceType="Framework" convention from the SWA SDK (requires dotnet/sdk#53135).
Changes:
- New
_WasmFrameworkAssetPathproperty (obj/fx/{PackageId}/) for materializing pass-through assets - Pass-through files are partitioned from WebCil-converted files and copied to the per-project
obj/fx/{PackageId}/path, registered withSourceType="Framework"via a newDefineStaticWebAssetscall - Satellite (culture) framework assets are handled with culture subdirectories, matching the existing
ConvertDllsToWebcilbehavior
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
… solutions
Multi-WASM-client solutions crash with duplicate Identity keys in
DiscoverPrecompressedAssets because pass-through files (JS, maps, ICU,
native wasm) share the same NuGet cache path across projects.
Instead of manually copying pass-throughs, use the SWA Framework
SourceType convention: DefineStaticWebAssets registers them as
SourceType="Framework", then UpdatePackageStaticWebAssets materializes
them to per-project obj/fx/{SourceId}/ directories, transforming
metadata (SourceType→Discovered, SourceId→PackageId,
AssetMode→CurrentProject) and giving each project a unique Identity.
This approach:
- Eliminates manual Copy tasks from Browser.targets
- Leverages the SDK's MaterializeFrameworkAsset for timestamp-based
incremental copies
- Properly models the relationship: framework assets adopted by each
consuming project
- Works with and without WebCil enabled
Depends on dotnet/sdk#53135 for Framework SourceType support in the SDK.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
9b1f0a8 to
1c6e5b6
Compare
|
This will fail until the sdk with the Framework source type makes it to runtime |
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Outdated
Show resolved
Hide resolved
|
waiting on #125419 now |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
You can also share your feedback on Copilot code review. Take the survey.
|
@javiercn I know you have some follow-up planed to make this cleaner but it we need to get preview3 green asap, this is blocking dotnet/aspnetcore#65673 |
c45416b to
95cf3c0
Compare
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Show resolved
Hide resolved
...nuget/Microsoft.NET.Sdk.WebAssembly.Pack/build/Microsoft.NET.Sdk.WebAssembly.Browser.targets
Show resolved
Hide resolved
95cf3c0 to
a78c28f
Compare
lewing
left a comment
There was a problem hiding this comment.
Addressing review feedback:
Comment accuracy (copilot-reviewer, line 419): Fixed — updated comment to accurately describe what UpdatePackageStaticWebAssets changes (SourceType, AssetMode, Identity/path remapping) without claiming SourceId→PackageId since both are already $(PackageId).
IsEnabled=false DLL handling (copilot-reviewer, line 67): Good point about the multi-client WasmEnableWebcil=false scenario. Currently when webcil is disabled, DLLs go through _WebcilAssetsCandidates as Computed with per-item ContentRoot, so Identity = their real NuGet cache path. In multi-client, this means two clients would get identical Identity for the same DLL — the original duplicate-Identity bug. However, this is the pre-existing behavior (before this PR, DLLs also went through the Computed path as-is). Fixing this properly requires routing shared DLLs through Framework materialization when IsEnabled=false, but the build targets Remove logic relies on DLLs being in WebcilCandidates for the publish path. I'll track this as a follow-up — the multi-client + NoWebcil combination is rare and the current PR doesn't regress it.
Output path clarification (@maraf, line 434): Updated comment to clarify — the materialized path is the intermediate one (obj/fx/{SourceId}/), and CopyToOutputDirectory=PreserveNewest causes the SDK to copy from there to bin/wwwroot/_framework/ at build time. UpdatePackageStaticWebAssets defaults CopyToOutputDirectory to Never, so without this override the framework files (dotnet.native.js, ICU data, etc.) don't make it to bin.
…ToOutputDirectory Two fixes: 1. ConvertDllsToWebcil: When IsEnabled=false, apply the same filtering for PassThroughCandidates as the enabled path — only non-DLL items without WasmNativeBuildOutput metadata qualify as Framework pass-throughs. Previously all candidates (including DLLs) were marked as pass-throughs, causing them all to go through Framework materialization which triggered NETSDK1147 workload detection in NoWebcil/NoWorkload test configurations. 2. Browser.targets: Set CopyToOutputDirectory=PreserveNewest on materialized framework assets after UpdatePackageStaticWebAssets. The SDK task defaults materialized assets to CopyToOutputDirectory= Never, but WASM framework files (dotnet.native.js, ICU data, etc.) must be copied to bin/wwwroot/_framework for the app to work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
a78c28f to
d92ebd8
Compare
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Show resolved
Hide resolved
712476d to
cf505eb
Compare
cf505eb to
62ab1d4
Compare
Validates that two Blazor WASM client projects can be built by a single server project without duplicate static web asset Identity collisions. This is the scenario that the Framework SourceType + UpdatePackageStaticWebAssets materialization path was designed to fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
62ab1d4 to
7847a96
Compare
src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks/ConvertDllsToWebCil.cs
Show resolved
Hide resolved
Add a publish variant of the existing MultiClientHostedBuild test to validate that multi-client hosted Blazor WASM scenarios work correctly during publish, not just build. The test creates two WASM clients with different StaticWebAssetBasePath values hosted by a single server project and verifies dotnet publish succeeds without duplicate Identity crashes. This complements the build-time test added in dotnet#125329 and ensures the Framework SourceType materialization path produces per-project Identity values that survive through the publish pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a publish variant of the existing MultiClientHostedBuild test to validate that multi-client hosted Blazor WASM scenarios work correctly during publish, not just build. The test creates two WASM clients with different StaticWebAssetBasePath values hosted by a single server project and verifies dotnet publish succeeds without duplicate Identity crashes. This complements the build-time test added in dotnet#125329 and ensures the Framework SourceType materialization path produces per-project Identity values that survive through the publish pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
When multiple Blazor WebAssembly client projects reference the same runtime pack, pass-through files (JS, maps, ICU data, native wasm) share a NuGet cache path. This causes duplicate Identity keys in the static web assets pipeline, crashing
DiscoverPrecompressedAssetswith anArgumentExceptiononToDictionary.Root Cause
PR #124125 correctly changed WASM
ContentRootfrom project-specificOutputPathcopies to per-item%(RootDir)%(Directory)(pointing to the real file in the NuGet cache). This fixed staleness on incremental builds. However, when two WASM client projects reference the same runtime pack, shared files likedotnet.js.mapnow resolve to identical NuGet cache paths, producing duplicate Identity values.Fix
Use the SWA Framework SourceType convention (dotnet/sdk#53135) to let the SDK handle materialization, rather than manual Copy tasks:
DefineStaticWebAssetsregisters pass-through files withSourceType="Framework"(using their NuGet cache paths)UpdatePackageStaticWebAssetsmaterializes them to per-projectobj/fx/{SourceId}/directories, transforming metadata:SourceType→DiscoveredSourceId→ projectPackageIdBasePath→ projectStaticWebAssetBasePathAssetMode→CurrentProjectIdentity→ materialized file path (unique per project)Asset classification:
SourceType="Computed"(already per-project inobj/webcil/)This eliminates manual Copy/culture-handling logic from Browser.targets and properly models the relationship: framework assets adopted by each consuming project.
Dependencies
Requires SDK support for
SourceType="Framework"from dotnet/sdk#53135.Testing
Validated with patched SDK (Framework SourceType + MaterializeFrameworkAsset) + Browser.targets:
WasmEnableWebcil=false): ✅Related