Repository navigation
Resolve trim and NativeAOT warnings in the other CSLA assemblies - #4969
Conversation
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>
There was a problem hiding this comment.
🟡 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)] |
There was a problem hiding this comment.
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.
| [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = Core.TrimmingMessages.PermissionPolicyJustification)] | ||
| [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = Core.TrimmingMessages.PermissionPolicyJustification)] |
There was a problem hiding this comment.
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.
| get => Utilities.CallByNameCore(Model, PropertyName, CallType.Get); | ||
| set => Utilities.CallByNameCore(Model, PropertyName, CallType.Set, value); |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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)] |
There was a problem hiding this comment.
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>


Summary
#4955 made
Csla.dlltrim 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 theFindDataPortalMethodleftover inCsla.dll.WinForms and WPF (Csla.Windows, Csla.Xaml.Wpf) can't be trimmed, so their analyzers stay off.
Changes
HttpPortal,GrpcPortal,RabbitMqPortal,WcfPortal) resolve client-supplied type names through a new internalMethodCaller.GetBusinessTypeCore, with[return: DynamicallyAccessedMembers(All)]. This uses the sameTypeNameResolutionJustificationcontract MobileFormatter already relies on when it deserializes those requests. Apps must preserve their business types, and this is now documented.InternalsVisibleToentries for Csla.AspNetCore, the three channels, Csla.Web.Mvc and Csla.Xaml.PolyUseEmbeddedAttributekeeps Csla's internal polyfills from clashing with the polyfills in friend assemblies. Csla.Testing now references Polyfill itself; it had been borrowing Csla's.PropertyInfo: calls a new internalUtilities.CallByNameCore, since its model is a business object.CslaPolicy.GetPolicy,HasPermissionAttribute, bothCslaPermissionRequirementtypes,PageModel<T>and theHtmlExtensionspermission helpersRabbitMqPortalOptionsRabbitMqProxyoverrides, which now match their baseSessionManager: uses a source-generatedJsonSerializerContextwith 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.Xamlidentity differed from the WPF and Avalonia builds, and it couldn't be a friend assembly.CslaModelBinder: suppressed withMvcJustification, because MVC and Razor Pages don't support trimming and the overrides can't carry the attribute.FindDataPortalMethod<T>/TryFindDataPortalMethod<T>Typeoverloads: now requireDynamicallyAccessedMembers.All, which matches what they reflect over. The internal callers bounded by theIDataPortalServercontract suppress withBusinessTypeJustificationinstead of widening public interfaces.CslaTestHostgeneric annotations.RuleCachepreservesLazy<T>properties throughDynamicDependency.Csla.AotSmokeTest: a new in-process HttpPortal round trip.HttpProxysends requests through anHttpMessageHandlerto a server-sideHttpPortal, covering create, fetch, update and delete.Testing
csla.build.slnwith the analyzers on: 0 IL warnings in the analyzed projects, confirmed with a--no-incrementalrebuild.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.Closes #4962
🤖 Generated with Claude Code