Skip to content

Resolve trim and NativeAOT warnings in the other CSLA assemblies - #4969

Merged
rockfordlhotka merged 3 commits into
mainfrom
4962-trim-aot-other-assemblies
Oct 28, 2026
Merged

rockfordlhotka merged 3 commits into
mainfrom
4962-trim-aot-other-assemblies

Conversation

@rockfordlhotka

@rockfordlhotka rockfordlhotka commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

#4955 made Csla.dll trim and NativeAOT clean, and turned the late-bound reflection APIs into [RequiresUnreferencedCode]. The other CSLA assemblies call those APIs and had never been analyzed. This PR turns the trim, AOT and single-file analyzers on for Csla.Blazor, Csla.Blazor.WebAssembly, Csla.AspNetCore, Csla.Xaml (MAUI), Csla.Channels.Grpc, Csla.Channels.RabbitMq, Csla.Channels.Wcf and Csla.Testing, and resolves all 90 or so warnings they reported. It also clears up the FindDataPortalMethod leftover in Csla.dll.

WinForms and WPF (Csla.Windows, Csla.Xaml.Wpf) can't be trimmed, so their analyzers stay off.

Changes

  • Data portal hosts (HttpPortal, GrpcPortal, RabbitMqPortal, WcfPortal) resolve client-supplied type names through a new internal MethodCaller.GetBusinessTypeCore, with [return: DynamicallyAccessedMembers(All)]. This uses the same TypeNameResolutionJustification contract MobileFormatter already relies on when it deserializes those requests. Apps must preserve their business types, and this is now documented.
  • New InternalsVisibleTo entries for Csla.AspNetCore, the three channels, Csla.Web.Mvc and Csla.Xaml. PolyUseEmbeddedAttribute keeps Csla's internal polyfills from clashing with the polyfills in friend assemblies. Csla.Testing now references Polyfill itself; it had been borrowing Csla's.
  • Blazor PropertyInfo: calls a new internal Utilities.CallByNameCore, since its model is a business object.
  • Annotations to satisfy callees:
    • CslaPolicy.GetPolicy, HasPermissionAttribute, both CslaPermissionRequirement types, PageModel<T> and the HtmlExtensions permission helpers
    • The Blazor/WASM session type options and RabbitMqPortalOptions
    • The RabbitMqProxy overrides, which now match their base
  • Blazor WebAssembly SessionManager: uses a source-generated JsonSerializerContext with web defaults, so the wire format is unchanged.
  • Csla.Xaml.PropertyInfo: walks arbitrary binding paths, so it is now [RequiresUnreferencedCode], matching MAUI's own string-path bindings.
  • Csla.Xaml.Maui is now strong-named with the CSLA key. It was the only CSLA package that wasn't, so its Csla.Xaml identity differed from the WPF and Avalonia builds, and it couldn't be a friend assembly.
  • MVC tag helpers and CslaModelBinder: suppressed with MvcJustification, because MVC and Razor Pages don't support trimming and the overrides can't carry the attribute.
  • FindDataPortalMethod<T> / TryFindDataPortalMethod<T> Type overloads: now require DynamicallyAccessedMembers.All, which matches what they reflect over. The internal callers bounded by the IDataPortalServer contract suppress with BusinessTypeJustification instead of widening public interfaces.
  • Csla.Testing: CslaTestHost generic annotations. RuleCache preserves Lazy<T> properties through DynamicDependency.
  • Csla.AotSmokeTest: a new in-process HttpPortal round trip. HttpProxy sends requests through an HttpMessageHandler to a server-side HttpPortal, covering create, fetch, update and delete.
  • Docs: a new section in "Upgrading to CSLA 11" covers the host type-preservation requirement and the new annotations.

Testing

  • csla.build.sln with the analyzers on: 0 IL warnings in the analyzed projects, confirmed with a --no-incremental rebuild.
  • csla.maui.build.sln: all 8 MAUI target frameworks build with 0 warnings and 0 errors.
  • dotnet test Source/csla.test.sln --filter TestCategory!=SkipOnCIServer --settings Source/test.runsettings: every test assembly passes, with 0 failures.
  • AOT smoke test:
    • The trimmed publish has 0 IL warnings, and all 16 scenarios pass, including the new host round trip.
    • The NativeAOT publish (ILC) reports no IL/AOT warnings. This machine has no C++ linker, so the native exe wasn't linked or run locally; CI covers that run.

Closes #4962

🤖 Generated with Claude Code

rockfordlhotka and others added 2 commits October 28, 2026 22:24
Enable the trim, AOT and single-file analyzers in Csla.Blazor,
Csla.Blazor.WebAssembly, Csla.AspNetCore, Csla.Xaml (MAUI),
Csla.Channels.Grpc, Csla.Channels.RabbitMq, Csla.Channels.Wcf and
Csla.Testing, and resolve every warning they report.

- Data portal hosts resolve client type names through the internal
  MethodCaller.GetBusinessTypeCore, under the same contract MobileFormatter
  already relies on. New InternalsVisibleTo entries for the hosts;
  PolyUseEmbeddedAttribute keeps Csla's polyfills from clashing with theirs.
- Blazor PropertyInfo uses the new internal Utilities.CallByNameCore.
- Permission policy, PageModel<T>, option and proxy override types carry
  the DynamicallyAccessedMembers their callees require.
- Blazor WebAssembly SessionManager uses source-generated JSON metadata.
- Csla.Xaml PropertyInfo walks arbitrary binding paths, so it is marked
  RequiresUnreferencedCode. TrimmingMessages is linked into Csla.Xaml
  because the MAUI assembly isn't strong-named.
- MVC tag helpers and CslaModelBinder suppress with MvcJustification.
- The Type overloads of FindDataPortalMethod<T>/TryFindDataPortalMethod<T>
  require DynamicallyAccessedMembers.All, matching what they reflect over.
- Csla.AotSmokeTest adds an in-process HttpPortal round trip.
- Document the changes in "Upgrading to CSLA 11".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Csla.Xaml.Maui was the only CSLA package not strong-named, so its
Csla.Xaml assembly identity differed from the WPF and Avalonia builds and
it couldn't be a friend assembly of Csla. Sign it with the CSLA key, and
replace the linked copy of TrimmingMessages.cs with an InternalsVisibleTo
entry for Csla.Xaml, like the other CSLA packages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The MAUI Release dependency prevents compilation, and suppressed trimming requirements can leave reflected members or policy types unpreserved.

5 open issues
What changed in this PR

Extends the trimming and NativeAOT work from #4955 to CSLA’s UI, hosting, channel, and testing assemblies, addressing #4962.

Changes:

  • Enables analyzers and adds preservation annotations, internal reflection helpers, and targeted suppressions.
  • Uses source-generated JSON for WebAssembly session messages and strong-names the MAUI assembly.
  • Adds an HTTP portal smoke-test scenario and documents application preservation requirements.
File Description
Source/​tests/​Csla.AotSmokeTest/​Program.cs Adds HTTP portal CRUD coverage.
Source/​tests/​Csla.AotSmokeTest/​Models.cs Adds the hosted smoke-test model.
Source/​tests/​Csla.AotSmokeTest/​HttpPortalHandler.cs Routes requests to an in-process host.
Source/​tests/​Csla.AotSmokeTest/​Csla.AotSmokeTest.csproj References ASP.NET Core hosting support.
Source/​Csla/​Utilities.cs Adds an internal business-object invocation helper.
Source/​Csla/​Server/​FactoryDataPortal.cs Adds a targeted trimming suppression.
Source/​Csla/​Reflection/​ServiceProviderMethodCallerExtensions.cs Suppresses internal lookup contract warnings.
Source/​Csla/​Reflection/​ServiceProviderMethodCaller.cs Requires preservation of all target members.
Source/​Csla/​Reflection/​MethodCaller.cs Adds business-type name resolution.
Source/​Csla/​Properties/​AssemblyInfo.cs Grants additional friend-assembly access.
Source/​Csla/​Csla.csproj Hides embedded polyfills from friend assemblies.
Source/​Csla/​Core/​TrimmingMessages.cs Centralizes new warning and suppression messages.
Source/​Csla.Xaml.Shared/​PropertyInfo.cs Exposes binding-path trimming requirements.
Source/​Csla.Xaml.Maui/​Csla.Xaml.Maui.csproj Enables analyzers and assembly signing.
Source/​Csla.Web.Mvc.Shared/​Server/​Hosts/​HttpPortal.cs Uses internal business-type resolution.
Source/​Csla.Web.Mvc.Shared/​HtmlExtensions.cs Annotates permission helper type parameters.
Source/​Csla.Web.Mvc.Shared/​CslaPermissionRequirement.cs Preserves authorization type constructors.
Source/​Csla.Web.Mvc.Shared/​CslaModelBinder.cs Adds MVC-specific trimming suppressions.
Source/​Csla.Testing/​Rules/​RuleCache.cs Preserves reflected cache fields and lazy properties.
Source/​Csla.Testing/​Hosting/​CslaTestHost.cs Annotates portal generic type parameters.
Source/​Csla.Testing/​Csla.Testing.csproj Enables analyzers and references Polyfill directly.
Source/​Csla.Channels.Wcf/​Server/​WcfPortal.cs Uses internal business-type resolution.
Source/​Csla.Channels.Wcf/​Csla.Channels.Wcf.csproj Adds conditional analyzer settings.
Source/​Csla.Channels.RabbitMq/​RabbitMqProxy.cs Matches base-method preservation annotations.
Source/​Csla.Channels.RabbitMq/​RabbitMqPortalOptions.cs Preserves portal factory constructors.
Source/​Csla.Channels.RabbitMq/​RabbitMqPortal.cs Uses internal business-type resolution.
Source/​Csla.Channels.RabbitMq/​Csla.Channels.RabbitMq.csproj Enables compatibility analyzers.
Source/​Csla.Channels.Grpc/​GrpcPortal.cs Uses internal business-type resolution.
Source/​Csla.Channels.Grpc/​Csla.Channels.Grpc.csproj Enables compatibility analyzers.
Source/​Csla.Blazor/​PropertyInfo.cs Uses internal reflection helpers and annotations.
Source/​Csla.Blazor/​HasPermissionAttribute.cs Preserves authorization type constructors.
Source/​Csla.Blazor/​CslaPolicy.cs Annotates policy types and suppresses resolution warnings.
Source/​Csla.Blazor/​CslaPermissionRequirement.cs Annotates the stored authorization type.
Source/​Csla.Blazor/​Csla.Blazor.csproj Enables compatibility analyzers.
Source/​Csla.Blazor/​BlazorServerConfigurationOptions.cs Preserves session service constructors.
Source/​Csla.Blazor.WebAssembly/​State/​SessionManagerJsonContext.cs Adds generated session JSON metadata.
Source/​Csla.Blazor.WebAssembly/​State/​SessionManager.cs Uses generated JSON serialization metadata.
Source/​Csla.Blazor.WebAssembly/​Csla.Blazor.WebAssembly.csproj Enables compatibility analyzers.
Source/​Csla.Blazor.WebAssembly/​Configuration/​BlazorWebAssemblyConfigurationOptions.cs Preserves session manager constructors.
Source/​Csla.AspNetCore/​RazorPages/​PageModel.cs Annotates the model type parameter.
Source/​Csla.AspNetCore/​PropertyInfo.cs Adds public-property preservation metadata.
Source/​Csla.AspNetCore/​Mvc/​TagHelpers/​CslaWarningsTagHelper.cs Adds an MVC-specific trimming suppression.
Source/​Csla.AspNetCore/​Mvc/​TagHelpers/​CslaInformationTagHelper.cs Adds an MVC-specific trimming suppression.
Source/​Csla.AspNetCore/​Mvc/​TagHelpers/​CslaErrorsTagHelper.cs Adds an MVC-specific trimming suppression.
Source/​Csla.AspNetCore/​Csla.AspNetCore.csproj Enables compatibility analyzers.
docs/​Upgrading to CSLA 11.md Documents preservation requirements and annotation changes.

Review effort: Balanced

/// <summary>
/// Expose metastate information about a property.
/// </summary>
[RequiresUnreferencedCode(TrimmingMessages.BindingPathReflection)]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: the Release configuration still references the Csla 10.0.0 package, so a Release build of Csla.Xaml.Maui can't see the new constant or the Csla.Xaml friend assembly entry. This isn't new to this PR, though. Building Release on main already fails because ViewModelBase uses Csla.Mvvm (#4960), which also doesn't exist in Csla 10. CI only builds MAUI in Debug, which uses the project reference, so it didn't catch either one.

The fix is to point the Release reference at Csla 11 once a Csla 11 package (prerelease or final) is published. That's a release-process decision, so I'm leaving it out of this PR. The TrimmingMessages constants are compiled into the attribute, so there's no runtime dependency on Csla internals.

Comment on lines +43 to +44
[UnconditionalSuppressMessage("Trimming", "IL2057", Justification = Core.TrimmingMessages.PermissionPolicyJustification)]
[UnconditionalSuppressMessage("Trimming", "IL2072", Justification = Core.TrimmingMessages.PermissionPolicyJustification)]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, the justification overclaimed. Policy names built by HasPermissionAttribute or CslaPolicy.GetPolicy do preserve the type, because their objectType parameter is annotated. A Csla: policy name written by hand in [Authorize(Policy = ...)] or configuration doesn't.

[RequiresUnreferencedCode] can't go on CslaPermissionsPolicyProvider.GetPolicyAsync, because it implements IAuthorizationPolicyProvider. So 5ce31cc rewrites the justification to say exactly that, and documents the requirement for hand-written policy names in "Upgrading to CSLA 11". That's the same model as string-named [ObjectFactory] types.

Comment on lines +72 to +73
get => Utilities.CallByNameCore(Model, PropertyName, CallType.Get);
set => Utilities.CallByNameCore(Model, PropertyName, CallType.Set, value);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is deliberate. Blazor PropertyInfo is how ViewModel<T>.GetPropertyInfo exposes a CSLA business object's properties, and the CSLA base classes preserve every member of a business type. Marking it [RequiresUnreferencedCode] would put a warning on every Blazor app for the uncommon case of a POCO model. The suppression uses BusinessTypeJustification, which already says other types must be preserved by the application.

You're right that this wasn't visible to users, so 5ce31cc documents it in "Upgrading to CSLA 11": a non-CSLA model used with PropertyInfo needs its public properties preserved.

[UnconditionalSuppressMessage("Trimming", "IL2072", Justification = Csla.Core.TrimmingMessages.BaseTypeWalkJustification)]
[UnconditionalSuppressMessage("Trimming", "IL2075", Justification = Csla.Core.TrimmingMessages.BaseTypeWalkJustification)]
private ServiceProviderMethodInfo? FindDataPortalMethod<T>([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type targetType, object?[]? criteria, bool throwOnError)
private ServiceProviderMethodInfo? FindDataPortalMethod<T>([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type targetType, object?[]? criteria, bool throwOnError)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a regression. The IL2070 suppression and the PublicConstructors return contract on IDataPortalActivator.ResolveType both come from #4955. This PR only widens the annotation on targetType to All and keeps that suppression as it was. A type the activator resolves is still a business type that the data portal operates on, so it derives from a CSLA base class annotated All.

Making ResolveType return All would be more precise, but it changes a public interface contract, and every custom activator would get IL2093 warnings. I'd treat that as a separate v11 decision rather than part of this PR.

return GetDataPortalMethodInfoFor<TAttribute>(source, objectType, EmptyCriteria.Instance);
}

[UnconditionalSuppressMessage("Trimming", "IL2067", Justification = Core.TrimmingMessages.BusinessTypeJustification)]

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This boundary was chosen on purpose. Before this PR, these extensions passed a PublicConstructors type into a PublicConstructors overload that suppressed IL2070 inside with the same BusinessTypeJustification. The assumption hasn't changed, only where the suppression sits. The only new thing is that the public Type overloads now honestly require All.

Carrying All through IDataPortalServer, DataPortalProxy and IDataPortalActivator would put IL2092 warnings on every custom proxy and server implementation. An ICslaObject that doesn't derive from a CSLA base class isn't a supported business type for the data portal. If we want the stricter contract, it fits better as a separate v11 change to the public interfaces.

…opertyInfo models

Address review feedback: a Csla: permission policy name can be written by
hand, so it isn't always produced by CslaPolicy.GetPolicy. Make the
suppression justification accurate and document that hand-written policy
names and non-CSLA Blazor PropertyInfo models must be preserved by the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rockfordlhotka
rockfordlhotka merged commit 165d9ca into main Oct 28, 2026
3 checks passed
@rockfordlhotka
rockfordlhotka deleted the 4962-trim-aot-other-assemblies branch October 28, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resolve trimming and NativeAOT warnings in the other CSLA assemblies

2 participants