We run a non-tiered ABP Commercial Blazor Web App. The WASM/Auto island carries the commercial Pro admin surfaces (Account.Pro, Identity.Pro, Saas.Host, OpenIddict.Pro, AuditLogging, SettingManagement, FeatureManagement, LanguageManagement, TextTemplateManagement, Gdpr, plus the LeptonX WASM theme). We deploy as Linux containers (mcr.microsoft.com/dotnet/aspnet:10.0) via Docker Compose (Coolify) and GitHub Actions. Host is framework-dependent with PublishReadyToRun=true, SatelliteResourceLanguages=en; publish is dotnet publish -c Release -r linux-x64 --no-self-contained.
We want to reduce the WASM cold-start payload and improve production startup, and we need your official position on which .NET 10 publish optimizations are supported/recommended with ABP Commercial 10.4 before we change our Dockerfile/publish. We're aware trimming and single-file had problems with ABP in the past — we want to know what's changed for ABP 10 / .NET 10.
What we've already measured (untrimmed Release, brotli, our app):
Published _framework brotli total 14.3 MB (461 files); cold wire transfer 8.3 MB across 184 files, ~29.6 MB decoded. Full IL trimming reduces _framework brotli to 9.6 MB (−33%, 356 files) and the build succeeds and boots — but the trim-analysis pass (-p:SuppressTrimAnalysisWarnings=false) reports 918 IL2xxx warnings, ~490 of them inside ABP's reflection core: Castle.DynamicProxy (132), Autofac (95), Volo.Abp (273) — interceptors, dynamic HTTP client proxies, and reflection-based DI. The rest are app/3rd-party. TrimMode=partial is a no-op on Blazor WASM for us — measured identical size (9.6 MB) and identical 918 warnings to full trim. We must keep BlazorWebAssemblyLoadAllGlobalizationData=true (see Q8). Questions
A. IL Trimming (PublishTrimmed)
Is full IL trimming officially supported and tested for ABP Commercial 10.4 Blazor WASM, including the dynamic HTTP client proxies, Castle DynamicProxy interception, reflection-based DI, object mapping, and the Pro modules listed above? Does ABP ship (or plan to ship) trimmer-safety metadata for its reflection-heavy paths — [DynamicDependency], ILLink.Descriptors.xml, [DynamicallyAccessedMembers], [RequiresUnreferencedCode], or feature switches — so those ~490 Castle/Autofac/Volo.Abp warnings are annotated-safe rather than something each customer must validate and re-validate on every ABP bump? Our hard blocker is that trimming can silently drop un-rooted DTO JSON properties (no exception — just missing fields over the dynamic-proxy wire). What is the recommended pattern to guarantee Application.Contracts DTO round-trip safety under trimming — rooting Contracts via TrimmerRootAssembly, an ABP-provided descriptor, a System.Text.Json source-generator context, or something else? Since TrimMode=partial is a no-op on Blazor WASM, is there an ABP-sanctioned way to trim the .NET framework/runtime assemblies while leaving Volo.Abp.* and Castle.* whole, to capture the framework savings without the reflection risk surface? B. WASM AOT (RunAOTCompilation) 5. Is RunAOTCompilation=true supported/recommended for ABP Commercial 10.4 WASM? AOT requires trimming and disables runtime IL generation — does ABP's WASM interception/proxy path (Castle DynamicProxy uses Reflection.Emit) remain functional under AOT, or does ABP rely solely on static/source-generated client proxies there? What size and startup deltas have you measured on a representative commercial app?
C. Single-file & publish packaging 6. Is PublishSingleFile=true (and trimming the server host) supported for the ABP host, given the embedded resources / Virtual File System and the LeptonX bundling pipeline? Single-file historically broke embedded-resource/IFileProvider resolution — is that resolved on ABP 10 / .NET 10? 7. Our published _framework has 461 files (356 trimmed). Is WasmEnableWebcil (default) the recommended packaging, and is there a supported way to reduce WASM request count (assembly bundling) on ABP 10.4?
D. Globalization data 8. We keep BlazorWebAssemblyLoadAllGlobalizationData=true because ABP applies the user/tenant culture dynamically during WASM startup; with it false we get "Blazor detected a change in the application's culture that is not supported" on every load. Is there an ABP-supported way to do dynamic culture switching with a bounded culture set (predefined supported cultures / custom ICU shard) so we don't ship the full ICU dataset? We only need en and es-* (Latin America).
E. Lazy loading Pro module assemblies 9. Can the commercial Pro module assemblies (Identity.Pro, Saas.Host, AuditLogging, OpenIddict.Pro, LanguageManagement, TextTemplateManagement, Gdpr — admin-only surfaces) be BlazorWebAssemblyLazyLoad-ed so they're fetched on-demand per route instead of in the cold-boot closure, given the ABP module system discovers/initializes modules from loaded assemblies at startup? If not supported today, is route-based code-splitting on the roadmap?
F. Compressed static assets & serving (we run behind a reverse proxy) 10. Is ABP's host static-file + LeptonX bundling pipeline fully compatible with .NET 10 compressed static web assets (build-time .br/.gz + fingerprinted import maps / MapStaticAssets)? In a containerized deploy behind a reverse proxy, should Kestrel serve the precompressed _framework/*.br, or should we offload compression/caching to the proxy? Recommended cache-control/immutable headers for the fingerprinted WASM assets?
G. Server image / runtime 11. Beyond PublishReadyToRun=true, what server-side production knobs do you recommend for the ABP host on .NET 10 containers (host trimming, R2R composite, tiered compilation, runtime/GC settings)? InvariantGlobalization is off the table for us (multi-culture) — anything else ABP-specific that interferes?
What would close this out for us: a concrete, supported set of publish properties we can pin in the .csproj / Dockerfile, and — ideally — a support matrix marking each of the above as supported / works-with-caveats / not supported on ABP Commercial 10.4 + .NET 10, plus a link to an official trimmed/AOT ABP Commercial Blazor WASM reference if one exists.
2 Answer(s)
-
0
Please contact us at https://abp.io/contact.
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
The short answer on the cold-start side: full IL trimming and WASM AOT won't work cleanly with ABP, so they're not the lever to pull here. ABP's runtime is reflection- and proxy-heavy by design — convention-based DI scans module assemblies/types/attributes at startup, UoW/auditing/validation interception goes through Castle DynamicProxy, and DTO serialization over the client proxies is reflection-based
System.Text.Json(a plainDefaultJsonTypeInfoResolver, no source-generatedJsonSerializerContext). The ~490 warnings you measured inCastle.DynamicProxy/Autofac/Volo.Abpland on exactly that surface. We don't ship trimmer-safety metadata ([DynamicDependency],ILLink.Descriptors.xml,[DynamicallyAccessedMembers],[RequiresUnreferencedCode]) for those paths, so those warnings aren't noise you can wave through — they're real, unannotated trim risks, including the silent DTO-drop case you're worried about.Going through your list:
| Area | Where it lands | |------|------| | A.
PublishTrimmed(WASM) | Don't. No trimmer-safety metadata is shipped; convention-based DI scanning, Castle interception and reflection-based STJ all rely on metadata the trimmer can remove. | | A3. DTO round-trip under trimming | Not a safe boundary. Serialization is reflection-based and ABP doesn't root the DTO contracts, so round-trips aren't guaranteed. There's no ABP-provided descriptor or source-gen context to root them. | | A4. Trim runtime only, keepVolo.Abp.*/Castle.*whole | You can root them withTrimmerRootAssembly, but that mainly turns trimming back off for the risky ABP surface — it preserves metadata/code rather than making the reflection paths trim-safe. The remaining saving comes from the framework and other non-rooted assemblies, and it's not a config we test, so you'd own the validation. | | B.RunAOTCompilation(WASM) | Same trim-analysis problem, plus Castle-based interception and any dynamic HTTP client-proxy path use runtime proxy generation, so AOT isn't a clean switch for this stack. The static Pro client proxies avoid the dynamic HTTP-proxy generation, but they still use the same reflection-based JSON and ABP startup surface. | | C.PublishSingleFile(host) | Not a useful shape here. The executable won't actually carry the Blazor/static payload as one file — static web assets,wwwrootcontent, generated_frameworkassets and the compressed files stay as sidecar files next to it. Use the normal folder/container publish output for the host. | | C7.WasmEnableWebcil| Keep the default (true) — that's the right packaging. | | D. Bounded ICU / custom culture shard | Not something ABP provides or tests. The template keepsBlazorWebAssemblyLoadAllGlobalizationDataon because the WASM client gets its culture from the server and switches among the configured languages at runtime; keep it on unless you fully own and test a custom culture set. | | E.BlazorWebAssemblyLazyLoadfor Pro modules | Won't work for the Pro modules. Blazor lazy loading can defer ordinary route assemblies, but the modules in the[DependsOn]graph are startup dependencies — their types have to be available before ABP startup runs, since each module'sConfigureServicesexecutes at app init. So admin modules can't be split out per route while they're ABP modules in the startup graph. | | F. Compressed static assets behind a proxy | Works. ABP 10 usesMapAbpStaticAssets, which serves the ABP virtual-file/dynamic bundle files and then calls ASP.NET CoreMapStaticAssets. Serving notes below. | | G. Server-sidePublishReadyToRun| Fine for the host (R2R doesn't strip metadata). Publish it for your deploy RID (-r linux-x64). Just don't combine the host with trimming or NativeAOT — same reflection reasons as the WASM side. |A concrete set you can pin. These split across two projects — the WASM knobs go in the client project, not the host:
<!-- host project (Microsoft.NET.Sdk.Web) --> <PropertyGroup> <PublishReadyToRun>true</PublishReadyToRun> </PropertyGroup><!-- WASM client project (Microsoft.NET.Sdk.BlazorWebAssembly) --> <PropertyGroup> <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData> <WasmEnableWebcil>true</WasmEnableWebcil> <!-- Do NOT enable PublishTrimmed, RunAOTCompilation or InvariantGlobalization for the ABP client. --> </PropertyGroup>SatelliteResourceLanguagesis worth setting only if you're intentionally limiting UI satellite resources to a fixed set of languages — with Language Management and runtime language switching in the picture, don't blanket-pin it toen.Where the cold-start wins actually are for your stack (no reflection risk):
- Keep WebCIL + Brotli (already produced by
dotnet publish -c Release). Serve the precompressed_framework/*.brdirectly and let the client negotiate; don't recompress at runtime. - Behind a reverse proxy, either Kestrel or the proxy can serve the precompressed framework files — pick one, just don't double-compress. Serve the fingerprinted WASM assets with
Cache-Control: public, max-age=31536000, immutable; the filenames change on rebuild, so long immutable caching is safe. - ABP's runtime-generated bundles (the LeptonX global bundles) are written into the dynamic file provider at startup and served through ABP's virtual/static-file middleware, not through the publish-time precompressed-asset pipeline — so treat those as normal static responses and let the proxy/Kestrel compress them if you want them compressed.
- Make sure HTTP/2 (or HTTP/3) is on at the proxy so the ~180
_frameworkrequests multiplex on one connection instead of paying per-request overhead.
So the lever for your setup is transport (WebCIL + Brotli + immutable caching + HTTP/2), not trimming/AOT.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - Keep WebCIL + Brotli (already produced by