Skip to content

Resolve all trim and AOT analyzer warnings - #4955

Open
rockfordlhotka wants to merge 8 commits into
mainfrom
issue-3347/add-requiresdynamiccode-attribute
Open

rockfordlhotka wants to merge 8 commits into
mainfrom
issue-3347/add-requiresdynamiccode-attribute

Conversation

@rockfordlhotka

@rockfordlhotka rockfordlhotka commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Summary

This builds on #4340, which turned on the trim/AOT analyzers for Csla.csproj and put every warning code in NoWarn. This PR resolves every trim/AOT analyzer warning in Csla and removes the NoWarn list entirely. Csla now builds with zero IL warnings on net10.0 and net11.0, with nothing suppressed project-wide.

It was also validated end to end with a self-contained, trimmed (PublishTrimmed) app that does all of the following, which surfaced and fixed real runtime trimming bugs (see below):

  • Create, Fetch (async Task<T>), Save, and command Execute through the data portal
  • Child lists and AddNew
  • MobileFormatter cloning and n-level undo
  • GraphMerger and LoadProperty(IPropertyInfo, object)
  • A read-only list fetch

Fixed rather than annotated (no runtime code generation or reflection on these paths)

  • LoadProperty / LoadPropertyMarkDirty(IPropertyInfo, object) (BusinessBase, ReadOnlyBase, ManagedObjectBase) dispatch through PropertyInfo<T> to the typed IManageProperties methods instead of MakeGenericMethod.
    • Reflection remains only as a fallback, guarded by RuntimeFeature.IsDynamicCodeSupported. It covers custom IPropertyInfo implementations and widening conversions.
    • The dead #if IOS workaround ladder is removed.
  • GraphMerger merges child collections through an internal IGraphMergeable interface implemented by BusinessListBase/BusinessBindingListBase, instead of MakeGenericMethod.
  • Task<T> data portal results are converted without a runtime generic instantiation.
  • Array.CreateInstanceFromArrayType on .NET 9+.
  • Dead code removed: ApplicationContext.CreateGenericInstance(DI) and Core/TypeSystem had no callers.

Trimming correctness fixes found by the trimmed test app

  • Data portal operation methods were trimmed from list types. IDataPortal<T>, IChildDataPortal<T>, DataPortal<T>, the portal factories, the operation invokers and ChildDataPortal's generic methods only required PublicConstructors on T, even though the data portal reflects over T's (private, possibly inherited) operation methods. They now require DynamicallyAccessedMembers.All, as do the "self" type parameters of BusinessListBase/BusinessBindingListBase and DynamicListBase's item type, matching BusinessBase<T>/ReadOnlyBase<T>. Before this, [Fetch] on a ReadOnlyListBase subclass failed in a trimmed app.
  • MobileFormatter couldn't recreate CSLA's own serializable types (FieldDataManager, BrokenRulesCollection, ...) because their public constructors were trimmed. IMobileObject is now annotated with PublicConstructors, and MobileFormatter gets each serialized object's type through an IMobileObject-typed, annotated helper, so the trimmer keeps the public constructor of every serialized type.
  • The default RevalidatingInterceptor lost its constructor, so the data portal failed to resolve interceptors. It's now registered through an annotated helper.

Annotated (inherently dynamic public API)

[RequiresDynamicCode] / [RequiresUnreferencedCode]:

  • MethodCaller.CallGenericMethod, CallGenericStaticMethodAsync, GetPropertyDescriptor, and the case-insensitive GetType(string, bool, bool) overload.
  • PocoSerializer<T>.

[RequiresUnreferencedCode]:

  • Constructors of the BindingList-based types (BindingList<T>() is itself [RequiresUnreferencedCode] in .NET), including NameValueListBase.
  • String-based Find/ApplySort/ApplyFilter on SortedBindingList/FilteredBindingList.
  • DataMapper.Map and its Get/SetFieldValue / SetPropertyValue helpers, and ObjectAdapter. These reflect over arbitrary objects.

[DynamicallyAccessedMembers] on:

  • MethodCaller's Type-taking public methods
  • PropertyInfo<T>'s containingType parameter
  • DataMap
  • Utilities.GetChildItemType
  • IObjectFactoryLoader.GetFactoryType (return value)
  • ITypeMap.SerializerType / TypeMap<T, S>
  • The CslaOptions / DataPortalServerOptions / MobileFormatterOptions registration generics
  • AuthorizeRequest.ObjectType

RegisterProperty(Type, PropertyInfo<P>) and RegisterMethod(Type, IMemberInfo) no longer put an annotation on objectType. Previously every class deriving from ReadOnlyBase/BusinessBase/ManagedObjectBase got IL2111 warnings in the user's build. A consumer project with IsAotCompatible=true now only sees warnings for NameValueListBase subclasses and DataMapper.Map.

Justified suppressions

Every suppression is an [UnconditionalSuppressMessage] with a justification. The shared justification texts are in Core/TrimmingMessages.cs.

  • Type-by-name resolution (MethodCaller.GetType, ObjectFactoryLoader, MobileFormatter, FieldDataManager deserialization). The names come from types CSLA serialized or from [ObjectFactory] attributes.
  • MethodCaller's object-based helpers and the data portal's reflection over business types. Business types are preserved by the data portal / base class annotations above.
  • Base-type walks for inherited members.
  • CoerceValue's last-resort TypeConverter fallback, which rethrows the original error if a converter was trimmed. CoerceValue's parameters no longer require DynamicallyAccessedMembers.All.
  • The DataAnnotation rule.
  • IL2112/IL2113 on the BindingList bases.

Removed obsolete APIs

The CslaXmlReader/CslaXmlWriter/CslaXmlBinaryReader/CslaXmlBinaryWriter types and CslaReaderWriterFactory.GetDataContractSerializer() are removed. They were obsolete since CSLA 9 (#4336), and DataContractSerializer isn't trim/AOT compatible. The breaking change is documented in docs/Upgrading to CSLA 11.md.

All annotation messages and suppression justifications live in Core/TrimmingMessages.cs. New exception messages are localized in Resources.resx.

Bug fixes found along the way

  • MethodCaller.FindMethod(Type, string, BindingFlags) looped forever when the method wasn't on the type or its direct base: it advanced with objType.BaseType instead of tmpType.BaseType.

Behavior notes

  • LoadProperty(IPropertyInfo, object) with a matching value type no longer wraps exceptions in TargetInvocationException. A subclass that hides (new) the generic LoadProperty<P> is no longer called through this path.
  • ManagedObjectBase.LoadPropertyMarkDirty(IPropertyInfo, object) previously threw for subclasses, and now works. CommandBase returns false, consistent with its existing IManageProperties behavior.
  • GraphMerger throws NotSupportedException for a child collection that isn't a BusinessListBase/BusinessBindingListBase.
  • Release notes:
    • AOT/trimmed apps that subclass BindingList-based types (including NameValueListBase) or call DataMapper.Map/ObjectAdapter now see IL2026 warnings.
    • Generic code that wraps IDataPortal<T>/IChildDataPortal<T> with its own type parameter needs [DynamicallyAccessedMembers(All)] on that parameter, where it previously needed PublicConstructors.
    • Custom IObjectFactoryLoader/ITypeMap implementations need the matching return/property annotations.

NativeAOT smoke test (new CI step)

Source/tests/Csla.AotSmokeTest (added to csla.test.sln) is a console app that CI publishes with NativeAOT after the unit tests. The step fails on any trim/AOT warning in the publish output, then runs the native exe, which exits non-zero if any scenario fails. The scenarios are:

  • Data portal: create, fetch, insert, update, delete (denied by object authorization) and command execute, including a Task<T>-returning operation.
  • Lists: editable and read-only child lists.
  • Rules: data annotation, lambda, async and authorization rules.
  • Other paths:
    • clone, n-level undo, MobileFormatter serialization to bytes
    • SaveAndMerge/GraphMerger, LoadProperty(IPropertyInfo, object)
    • ObjectFactory, and [CslaImplementProperties] generated properties

-p:CslaSmokeTrimOnly=true does a trimmed, self-contained publish for machines without the native linker. Both are documented in CLAUDE.md.

The smoke test found two more problems, both fixed here:

  • [ObjectFactory(typeof(X))] didn't preserve the factory's methods. The attribute's Type constructor parameters now require DynamicallyAccessedMembers.All.
  • Whole-program AOT analysis reported IL3050 for every enum or array property type, including in user code. [DynamicallyAccessedMembers(All)] on property-value generic parameters (P, PropertyInfo<T>, FieldData<T>, SerializationInfo.GetValue<T>, ...) reflection-enabled Enum.GetValues/Array.CreateInstance. Those ~200 annotations were unnecessary: removing them produces no analyzer warnings. Business object and data portal type parameters keep All.

Still not covered

The CI NativeAOT run covers the scenarios above in-process. Still untested under AOT:

  • remote data portal channels (HTTP, gRPC, RabbitMQ)
  • NameValueListBase and the Dynamic lists
  • the other packages (Csla.AspNetCore, Csla.Blazor, ...)

APIs marked [RequiresUnreferencedCode]/[RequiresDynamicCode] remain unsupported in trimmed/AOT apps. Declaring <IsAotCompatible>true</IsAotCompatible> is left as a separate decision.

Test plan

  • dotnet build Source/csla.test.sln: 0 errors, all TFMs
  • Csla builds with zero trim/AOT analyzer warnings (net10.0, net11.0), with no NoWarn
  • Full suite dotnet test Source/csla.test.sln --filter TestCategory!=SkipOnCIServer --settings Source/test.runsettings: all 23 test runs pass
  • New tests: LoadPropertyDispatchTests, ChildCollectionMergeTests (sync/async, nested BusinessListBase and BusinessBindingListBase), FindMethodHierarchyTests
  • Consumer project with IsAotCompatible=true (BusinessBase, ReadOnlyBase, list bases, CommandBase, NameValueListBase): only the intended IL2026 warnings
  • Self-contained PublishTrimmed app: the full scenario listed in the summary runs successfully
  • NativeAOT smoke test in CI: native publish with no trim/AOT warnings, and all 14 scenarios pass in the native exe

Closes #3347
Closes #4956

🤖 Generated with Claude Code

…rnings

Removes IL2026, IL2055, IL2057, IL2060, IL2096 and IL3050 from the Csla
NoWarn list and resolves every warning they covered:

- LoadProperty/LoadPropertyMarkDirty(IPropertyInfo, object) dispatch through
  PropertyInfo<T> to the strongly typed IManageProperties methods instead of
  MakeGenericMethod; reflection remains only as a fallback guarded by
  RuntimeFeature.IsDynamicCodeSupported
- GraphMerger merges child collections through IGraphMergeable implemented
  by BusinessListBase and BusinessBindingListBase instead of MakeGenericMethod
- Task<T> results are converted without a runtime generic instantiation
- Array.CreateInstanceFromArrayType on .NET 9+
- Removed unused CreateGenericInstance(DI) and TypeSystem
- Annotated inherently dynamic public API: MethodCaller.CallGenericMethod,
  CallGenericStaticMethodAsync, GetPropertyDescriptor, case-insensitive
  GetType, the obsolete DataContract readers/writers, PocoSerializer,
  BindingList based base class constructors, string based Find/ApplySort/
  ApplyFilter on SortedBindingList/FilteredBindingList, DataMapper.Map
- Justified suppressions for type-by-name resolution, the CoerceValue
  TypeConverter fallback and the DataAnnotation rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Removes the rest of the Csla NoWarn list (IL2070, IL2072, IL2075, IL2111);
Csla now builds with no trim/AOT analyzer warnings and nothing suppressed
project-wide. Validated with a self-contained trimmed app, which surfaced
runtime trimming bugs fixed here:

- The data portal reflects over T's operation methods but only required
  PublicConstructors; IDataPortal<T>, IChildDataPortal<T>, DataPortal<T>,
  the portal factories, operation invokers and ChildDataPortal now require
  DynamicallyAccessedMembers.All, as do the list base classes' self type
- IMobileObject requires PublicConstructors and MobileFormatter gets types
  through an annotated helper, so deserialization can recreate CSLA's own
  serializable types
- The default RevalidatingInterceptor keeps its constructor

Also:
- RegisterProperty/RegisterMethod(Type, ...) no longer annotate objectType,
  which produced IL2111 in every user business class
- Annotated MethodCaller Type APIs, PropertyInfo containingType, DataMap,
  GetChildItemType, IObjectFactoryLoader, ITypeMap, option registration
  generics, AuthorizeRequest; RequiresUnreferencedCode on DataMapper
  helpers and ObjectAdapter
- CoerceValue no longer requires DynamicallyAccessedMembers.All
- Justified suppressions for CSLA's reflection over business types
- Fixed MethodCaller.FindMethod(Type, string, BindingFlags) infinite loop

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rockfordlhotka rockfordlhotka changed the title Fix or annotate RequiresDynamicCode and RequiresUnreferencedCode warnings Resolve all trim and AOT analyzer warnings Oct 15, 2026
Source/tests/Csla.AotSmokeTest is a console app published with NativeAOT
that runs CSLA scenarios and exits non-zero on failure: data portal
create/fetch/insert/update/delete/execute (including Task<T> results),
editable and read-only child lists, data annotation, lambda, async and
authorization rules, object authorization, clone, undo, MobileFormatter
serialization, SaveAndMerge/GraphMerger, LoadProperty(IPropertyInfo),
ObjectFactory, and [CslaImplementProperties] generated properties.
CI publishes it after the tests, fails on any IL/AOT warning, and runs it.
-p:CslaSmokeTrimOnly=true does a trimmed self-contained publish for
machines without the native linker.

The trimmed run found that [ObjectFactory(typeof(T))] didn't preserve the
factory's methods; the attribute's Type constructor parameters now require
DynamicallyAccessedMembers.All.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rockfordlhotka
rockfordlhotka requested a review from a team as a code owner October 15, 2026 04:49
NativeAOT whole-program analysis in CI reported IL3050 for every enum or
array property type: [DynamicallyAccessedMembers(All)] on a generic
parameter keeps and reflection-enables all members of the type argument,
including System.Enum.GetValues(Type) and Array.CreateInstance, which are
RequiresDynamicCode. The warnings surfaced in user code (any enum
PropertyInfo<T>) and in Csla's own SerializationInfo.GetValue<T> calls.

Property value and field type parameters (P, F, R, PropertyInfo<T>,
FieldData<T>, property info factories, SerializationInfo.GetValue/AddValue,
IRuleContext, CoerceValue<D>) are never reflected over; removing the
annotation produces no analyzer warnings. Business object and data portal
type parameters keep DynamicallyAccessedMembers.All.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rockfordlhotka
rockfordlhotka requested review from kant2002 and a balanced review from Copilot October 15, 2026 05:08

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

Property-loading fallback and interceptor preservation issues remain, and the smoke test bypasses the new task-result conversion.

3 open findings
What changed in this PR

Improves CSLA’s trimming and NativeAOT support, following the analyzer work in #4340 and addressing #3347 and #4956.

Changes:

  • Replaces runtime generic reflection on key paths with typed dispatch.
  • Refines preservation annotations and removes project-wide trim/AOT warning suppressions.
  • Adds regression tests and a CI NativeAOT smoke test.
File Description
Source/​tests/​Csla.test/​PropertyInfo/​LoadPropertyDispatchTests.cs Tests untyped property loading.
Source/​tests/​Csla.test/​MethodCaller/​FindMethodHierarchyTests.cs Tests inherited method lookup.
Source/​tests/​Csla.test/​GraphMerge/​ChildCollectionMergeTests.cs Tests child collection merging.
Source/​tests/​Csla.AotSmokeTest/​Program.cs Runs published smoke scenarios.
Source/​tests/​Csla.AotSmokeTest/​Models.cs Supplies smoke-test business types.
Source/​tests/​Csla.AotSmokeTest/​Csla.AotSmokeTest.csproj Configures native and trimmed publishing.
Source/​Csla/​Utilities.cs Refines reflection and conversion annotations.
Source/​Csla/​SortedBindingList.cs Marks trim-unsafe property lookups.
Source/​Csla/​Server/​ObjectFactoryLoader.cs Annotates factory type resolution.
Source/​Csla/​Server/​ObjectFactoryAttribute.cs Preserves factory members.
Source/​Csla/​Server/​ObjectFactory.cs Removes property-value preservation requirements.
Source/​Csla/​Server/​IObjectFactoryLoader.cs Defines factory preservation contract.
Source/​Csla/​Server/​ChildDataPortalFactory.cs Preserves child business members.
Source/​Csla/​Server/​ChildDataPortal.cs Strengthens generic preservation requirements.
Source/​Csla/​Server/​AuthorizeRequest.cs Annotates business type metadata.
Source/​Csla/​Serialization/​Mobile/​TypeMap.cs Preserves serializer constructors.
Source/​Csla/​Serialization/​Mobile/​SerializationInfo.cs Removes value-type preservation requirements.
Source/​Csla/​Serialization/​Mobile/​MobileFormatter.cs Preserves serialized object constructors.
Source/​Csla/​Serialization/​Mobile/​ITypeMap.cs Defines serializer preservation contract.
Source/​Csla/​Serialization/​Mobile/​IMobileObject.cs Adds constructor preservation annotation.
Source/​Csla/​Serialization/​Mobile/​CustomSerializers/​PocoSerializer.cs Marks reflection serialization limitations.
Source/​Csla/​Serialization/​Mobile/​CslaXmlWriter.cs Marks trimming and AOT limitations.
Source/​Csla/​Serialization/​Mobile/​CslaXmlReader.cs Marks trimming and AOT limitations.
Source/​Csla/​Serialization/​Mobile/​CslaXmlBinaryWriter.cs Marks trimming and AOT limitations.
Source/​Csla/​Serialization/​Mobile/​CslaXmlBinaryReader.cs Marks trimming and AOT limitations.
Source/​Csla/​Serialization/​Mobile/​CslaReaderWriterFactory.cs Preserves reader/writer constructors.
Source/​Csla/​Serialization/​Mobile/​Configuration/​MobileFormatterOptions.cs Annotates reader/writer registration.
Source/​Csla/​Rules/​RuleContext.cs Removes value-type preservation requirements.
Source/​Csla/​Rules/​IRuleContext.cs Aligns rule context annotations.
Source/​Csla/​Rules/​CommonRules.cs Documents validation reflection suppression.
Source/​Csla/​Rules/​BusinessRules.cs Annotates validation metadata lookup.
Source/​Csla/​Rules/​AuthorizationRuleManager.cs Documents authorization reflection suppression.
Source/​Csla/​Reflection/​TaskConversionHelper.cs Reworks task-result extraction.
Source/​Csla/​Reflection/​ServiceProviderMethodInfo.cs Removes generic conversion metadata.
Source/​Csla/​Reflection/​ServiceProviderMethodCaller.cs Uses shared task conversion.
Source/​Csla/​Reflection/​MethodCaller.cs Refines reflection and fixes hierarchy traversal.
Source/​Csla/​Reflection/​DynamicMethodHandle.cs Removes generic conversion handles.
Source/​Csla/​ReadOnlyBindingListBase.cs Marks constructor trimming limitations.
Source/​Csla/​ReadOnlyBase.cs Uses typed property dispatch.
Source/​Csla/​PropertyInfo.cs Implements typed property loading.
Source/​Csla/​NameValueListBase.cs Marks constructor trimming limitations.
Source/​Csla/​IDataPortalT.cs Preserves business operation members.
Source/​Csla/​IDataPortalOperationInvoker.cs Aligns invoker preservation contracts.
Source/​Csla/​IDataPortalFactory.cs Strengthens portal preservation contract.
Source/​Csla/​IChildDataPortalT.cs Preserves child operation members.
Source/​Csla/​IChildDataPortalFactory.cs Strengthens child portal preservation contract.
Source/​Csla/​FilteredBindingList.cs Marks trim-unsafe property lookups.
Source/​Csla/​DynamicListBase.cs Preserves item operation members.
Source/​Csla/​DynamicBindingListBase.cs Annotates binding-list limitations.
Source/​Csla/​DataPortalT.cs Strengthens business type preservation.
Source/​Csla/​DataPortalClient/​DataPortalFactory.cs Aligns portal factory annotations.
Source/​Csla/​Data/​ObjectAdapter.cs Marks arbitrary reflection limitations.
Source/​Csla/​Data/​DataMapper.cs Marks trim-unsafe mapping APIs.
Source/​Csla/​Data/​DataMap.cs Preserves mapped members.
Source/​Csla/​Csla.csproj Removes project-wide IL suppressions.
Source/​Csla/​CriteriaBase.cs Removes property-value preservation requirements.
Source/​Csla/​Core/​UndoableHandler.cs Annotates reflected field requirements.
Source/​Csla/​Core/​UndoableBase.cs Documents hierarchy reflection suppressions.
Source/​Csla/​Core/​TypeSystem.cs Removes unused reflection helper.
Source/​Csla/​Core/​TrimmingMessages.cs Centralizes annotation explanations.
Source/​Csla/​Core/​ReadOnlyBindingList.cs Annotates binding-list limitations.
Source/​Csla/​Core/​PropertyLoader.cs Adds typed dispatch with reflection fallback.
Source/​Csla/​Core/​MobileBindingList.cs Marks constructor trimming limitations.
Source/​Csla/​Core/​ManagedObjectBase.cs Uses typed property dispatch.
Source/​Csla/​Core/​IPropertyInfoFactory.cs Relaxes property-value annotations.
Source/​Csla/​Core/​IManageProperties.cs Adds typed dirty-loading contract.
Source/​Csla/​Core/​IGraphMergeable.cs Defines typed collection merge dispatch.
Source/​Csla/​Core/​GraphMerger.cs Replaces reflective collection dispatch.
Source/​Csla/​Core/​FieldManager/​PropertyInfoManager.cs Relaxes property-value annotations.
Source/​Csla/​Core/​FieldManager/​FieldDataManager.cs Documents reflection preservation assumptions.
Source/​Csla/​Core/​FieldManager/​FieldData.cs Removes value-type preservation requirement.
Source/​Csla/​Core/​FieldManager/​DefaultPropertyInfoFactory.cs Aligns property factory annotations.
Source/​Csla/​Core/​ExtendedBindingList.cs Marks constructor trimming limitations.
Source/​Csla/​Core/​BusinessBase.cs Adds typed loading and preservation annotations.
Source/​Csla/​Configuration/​Fluent/​DataPortalServerOptions.cs Preserves interceptor constructors.
Source/​Csla/​Configuration/​Fluent/​DataPortalConfigurationExtensions.cs Isolates interceptor registration suppression.
Source/​Csla/​Configuration/​Fluent/​CslaOptions.cs Preserves exception-handler constructors.
Source/​Csla/​CommandBase.cs Implements typed dirty-loading behavior.
Source/​Csla/​BusinessListBase.cs Implements typed graph merging.
Source/​Csla/​BusinessDocumentBase.cs Relaxes property-value annotations.
Source/​Csla/​BusinessBindingListBase.cs Adds typed merging and trimming annotations.
Source/​Csla/​BusinessBase.cs Relaxes property registration annotations.
Source/​Csla/​ApplicationContext.cs Removes unused generic activation helpers.
Source/​csla.test.sln Includes the smoke-test project.
CLAUDE.md Documents publishing and annotation guidance.
.github/​workflows/​ci.yaml Publishes and runs NativeAOT smoke tests.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread Source/Csla/Configuration/Fluent/DataPortalServerOptions.cs
Comment thread Source/Csla/Core/PropertyLoader.cs Outdated
Comment thread Source/tests/Csla.AotSmokeTest/Models.cs Outdated
rockfordlhotka and others added 3 commits October 15, 2026 00:23
…coverage

- AddInterceptorProvider<T>(int index) now preserves T's public
  constructors like the parameterless overload
- PropertyLoader's reflection fallback resolves the generic method on
  IManageProperties, so interface dispatch reaches ManagedObjectBase's
  private and CommandBase's explicit implementations; previously
  LoadPropertyMarkDirty with a widening conversion threw on those types
- Regression tests for widening conversions on CommandBase and BusinessBase
- The smoke test's object factory returns Task<T> after a real suspension,
  so the NativeAOT run verifies the data portal's Task<T> result extraction

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The NativeAOT smoke test (with the object factory now returning Task<T>)
showed TaskConversionHelper failing in the native executable: it read
Result by reflection on the task's runtime type, an async state machine
box whose members have no reflection metadata under NativeAOT.

ConvertTask now walks to the Task<T> base type and reads Result there,
with a DynamicDependency that keeps Task<T>'s public properties.
Verified with a linux-x64 NativeAOT publish in a container (all smoke
scenarios pass); CLAUDE.md documents that local option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

🔵 Needs a closer look

Cross-cutting reflection, serialization, and public preservation-contract changes warrant final human review.

0 open findings

3 resolved since last review

🧠 Review effort: Balanced

Comment thread Source/Csla/Reflection/MethodCaller.cs Outdated
/// <see cref="Server.ObjectFactoryAttribute"/>, so they refer to types the
/// application uses. Trimmed applications must preserve those types.
/// </summary>
internal const string TypeNameResolutionJustification =

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.

Move this into TrimmingMessages.cs

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.

Done in 429960c. TypeNameResolutionJustification now lives in Core/TrimmingMessages.cs. The usages in MethodCaller, ObjectFactoryLoader, MobileFormatter and FieldDataManager reference it there.

Comment thread Source/Csla/Reflection/MethodCaller.cs Outdated
/// <param name="throwOnError">true to throw an exception if the type can't be found.</param>
/// <param name="ignoreCase">true for a case-insensitive comparison of the type name.</param>
/// <exception cref="ArgumentException"><paramref name="typeName"/> is <see langword="null"/>, <see cref="string.Empty"/> or only consists of white spaces.</exception>
[RequiresUnreferencedCode("A case-insensitive type lookup can't be analyzed for trimming. Use GetType(string, bool) instead.")]

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.

Use nameof() for the GetType method reference?

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.

Done in 429960c. The message is now TrimmingMessages.CaseInsensitiveTypeLookup, a const interpolated string: $"... Use {nameof(MethodCaller)}.{nameof(MethodCaller.GetType)}(string, bool) instead."

Comment thread Source/Csla/Reflection/MethodCaller.cs Outdated
/// <param name="parameters">Parameters for method</param>
/// <exception cref="ArgumentNullException"><paramref name="objectType"/> or <paramref name="typeParams"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"> is <see langword="null"/>, <see cref="string.Empty"/> or only consists of white spaces.</exception>
[RequiresDynamicCode("Creates a generic method instantiation from runtime types.")]

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.

Shouldn't they be moved into TrimmingMessages.cs as well?

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.

Done in 429960c. Moved to Core/TrimmingMessages.cs. I moved every other inline trimming message and justification this PR added there too (PropertyLoader, the interceptor registration, CoerceValue's fallback, ...), so the file now holds all RequiresUnreferencedCode/RequiresDynamicCode messages and UnconditionalSuppressMessage justifications, grouped into those two regions.

Comment thread Source/Csla/Reflection/MethodCaller.cs Outdated
/// <param name="parameters">Parameters for method</param>
/// <exception cref="ArgumentNullException"><paramref name="target"/> or <paramref name="typeParams"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"> is <see langword="null"/>, <see cref="string.Empty"/> or only consists of white spaces.</exception>
[RequiresDynamicCode("Creates a generic method instantiation from runtime types.")]

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.

Shouldn't they be moved into TrimmingMessages.cs as well?

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.

Done in 429960c. Moved to Core/TrimmingMessages.cs. I moved every other inline trimming message and justification this PR added there too (PropertyLoader, the interceptor registration, CoerceValue's fallback, ...), so the file now holds all RequiresUnreferencedCode/RequiresDynamicCode messages and UnconditionalSuppressMessage justifications, grouped into those two regions.

public static async Task<object?> ConvertTask<T>(Task<T?> task)

[UnconditionalSuppressMessage("Trimming", "IL2075",
Justification = "Result is a public property of Task<T>, preserved by the DynamicDependency on ConvertTask.")]

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.

Shouldn't they be moved into TrimmingMessages.cs as well?

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.

Done in 429960c. Moved to Core/TrimmingMessages.cs. I moved every other inline trimming message and justification this PR added there too (PropertyLoader, the interceptor registration, CoerceValue's fallback, ...), so the file now holds all RequiresUnreferencedCode/RequiresDynamicCode messages and UnconditionalSuppressMessage justifications, grouped into those two regions.

/// to read the data from a stream
/// </summary>
[Obsolete("This type of serialization is unsupported. It will be removed with one of the next major versions of CSLA.NET.")]
[RequiresDynamicCode("DataContractSerializer is not compatible with NativeAOT.")]

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.

Instead of annotating we should remove it now? Or when did we mark them obsolete?

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.

Agreed, removed in 429960c. They were marked [Obsolete] in v9.0.0 (#4336, Nov 2024), and nothing in the repo used them. This removes all four CslaXml* reader/writer types plus the also-obsolete CslaReaderWriterFactory.GetDataContractSerializer(). I added a breaking-change section to docs/Upgrading to CSLA 11.md: users who selected them through MobileReader<T>()/MobileWriter<T>() should drop those calls and update client and server together.

/// to write the data to a stream
/// </summary>
[Obsolete("This type of serialization is unsupported. It will be removed with one of the next major versions of CSLA.NET.")]
[RequiresDynamicCode("DataContractSerializer is not compatible with NativeAOT.")]

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.

Instead of annotating we should remove it now? Or when did we mark them obsolete?

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.

Agreed, removed in 429960c. They were marked [Obsolete] in v9.0.0 (#4336, Nov 2024), and nothing in the repo used them. This removes all four CslaXml* reader/writer types plus the also-obsolete CslaReaderWriterFactory.GetDataContractSerializer(). I added a breaking-change section to docs/Upgrading to CSLA 11.md: users who selected them through MobileReader<T>()/MobileWriter<T>() should drop those calls and update client and server together.

/// to read the data from a stream
/// </summary>
[Obsolete("This type of serialization is unsupported. It will be removed with one of the next major versions of CSLA.NET.")]
[RequiresDynamicCode("DataContractSerializer is not compatible with NativeAOT.")]

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.

Instead of annotating we should remove it now? Or when did we mark them obsolete?

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.

Agreed, removed in 429960c. They were marked [Obsolete] in v9.0.0 (#4336, Nov 2024), and nothing in the repo used them. This removes all four CslaXml* reader/writer types plus the also-obsolete CslaReaderWriterFactory.GetDataContractSerializer(). I added a breaking-change section to docs/Upgrading to CSLA 11.md: users who selected them through MobileReader<T>()/MobileWriter<T>() should drop those calls and update client and server together.

/// to write the data to a stream
/// </summary>
[Obsolete("This type of serialization is unsupported. It will be removed with one of the next major versions of CSLA.NET.")]
[RequiresDynamicCode("DataContractSerializer is not compatible with NativeAOT.")]

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.

Instead of annotating we should remove it now? Or when did we mark them obsolete?

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.

Agreed, removed in 429960c. They were marked [Obsolete] in v9.0.0 (#4336, Nov 2024), and nothing in the repo used them. This removes all four CslaXml* reader/writer types plus the also-obsolete CslaReaderWriterFactory.GetDataContractSerializer(). I added a breaking-change section to docs/Upgrading to CSLA 11.md: users who selected them through MobileReader<T>()/MobileWriter<T>() should drop those calls and update client and server together.

Comment thread Source/Csla/Utilities.cs Outdated

#region CoerceValue

private const string TypeConverterFallbackJustification =

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.

Shouldn't they be moved into TrimmingMessages.cs as well?

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.

Done in 429960c. Moved to Core/TrimmingMessages.cs. I moved every other inline trimming message and justification this PR added there too (PropertyLoader, the interceptor registration, CoerceValue's fallback, ...), so the file now holds all RequiresUnreferencedCode/RequiresDynamicCode messages and UnconditionalSuppressMessage justifications, grouped into those two regions.

…CslaXml*

- Move all RequiresUnreferencedCode/RequiresDynamicCode messages and
  UnconditionalSuppressMessage justifications added for trimming into
  Core/TrimmingMessages.cs; the case-insensitive GetType message uses
  nameof
- Localize the new exception messages (TaskConversionHelper,
  PropertyLoader, GraphMerger) in Resources.resx
- Remove the CslaXmlReader/Writer and CslaXmlBinaryReader/Writer types and
  CslaReaderWriterFactory.GetDataContractSerializer, obsolete since CSLA 9;
  documented in Upgrading to CSLA 11

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

This branch has not been deployed

No deployments
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 remaining trim analyzer warnings (DynamicallyAccessedMembers pass) Add RequiresDynamicCode attribute to dynamic methods/members

3 participants