Skip to content

Trim/AOT warning cleanup: Data/ dynamic-query engine, Base/ reflection helpers, HttpHandler/JSInteropAdaptor - #81

Open
PrinceOliver wants to merge 10 commits into
readiness-correctionsfrom
feat/cs-1bc799cf
Open

PrinceOliver wants to merge 10 commits into
readiness-correctionsfrom
feat/cs-1bc799cf

Conversation

@PrinceOliver

Copy link
Copy Markdown
Collaborator

📍 Agent Session: View Full Session | 👤 Requested by: @PrinceOliver | 🤖 Agent: Agent

📋 Task Context

Make the Blazor Toolkit component library clean of Blazor-specific (BLxxxx), Roslyn analyzer, IL Trimmer (IL2xxx), and Native AOT (IL3xxx) warnings — preferring real trim/AOT-safety fixes (annotations, refactors) over suppression, and propagating [RequiresUnreferencedCode]/[RequiresDynamicCode] up the call chain to the correct architectural boundary when reflection/dynamic code genuinely can't be removed.

📝 Implementation Summary

Three verifiable batches landed on this branch, each rebuilt and diffed against the previous state to confirm no regressions:

1. src/Base/SfBaseUtils.cs — replaced two JsonSerializer-based round-trips with reflection-free equivalents:

  • Equals<T>: array comparison now walks elements directly (ArraysEqual) instead of serializing both sides to JSON and comparing strings.
  • ChangeType: TimeSpan conversion now uses TimeSpan.Parse(...) instead of a serialize/deserialize round-trip.
    This eliminates the warnings entirely (no annotation needed) — the strongest fix per the stated priority order.

2. Data/ dynamic-query engine + Base/Reflection helpers (QueryableExtensions, DynamicQueryableExtensions, EnumerableExtensions, EnumerableOperation, QueryableOperation, DataOperations, DynamicObjectOperations, DataUtil, DataManager, Adaptors/AdaptorBase, Adaptors/BlazorAdaptor, Base/Reflection/FastReflectionExtension) — annotated the ~90 public string-property-name-based LINQ helpers and their private call chains with [RequiresUnreferencedCode]/[RequiresDynamicCode], propagated up to DataManager.ExecuteQuery/ExecuteQueryAsync/ProcessOfflineAsync (the public entry points of the dynamic-query engine — the correct stopping point within Data/).

3. src/Data/HttpHandler.cs + src/Data/JSInteropAdaptor.cs — annotated PrepareRequest, PrepareBatchRequest, and SyncfusionInterop.InvokeMethodAsync<T>, all of which call JsonSerializer.Serialize on statically-unknown-shape payloads (request bodies / JS interop arguments). Verified their existing callers in DataManager.cs already carry matching Requires* attributes (no new warnings there). InvokeMethodAsync is also called from BaseComponent.cs (Dispose, Refresh, interop helpers), which is not yet annotated — see Human Review Required below.

Net effect measured via dotnet build -f net9.0 before/after each batch: warnings removed at the true reflection/JSON call sites, with a small, deliberate, documented set of new warnings surfacing at un-annotated callers (expected — the analyzer is now honestly reporting the real risk instead of staying silent).

💡 Key Technical Decisions

  • Stop attribute propagation at DataManager's public entry points (ExecuteQuery/ExecuteQueryAsync/ProcessOfflineAsync) rather than continuing into every Components/ caller in this batch.
    • Why: These are the actual public API boundary of the dynamic-query engine within Data/. Continuing further would require touching every consuming component in one giant, unreviewable diff instead of small verifiable batches.
    • Alternative considered: Propagate all the way through every Components/ call site in a single commit — rejected as too large to verify safely in one pass.
  • Did not add [RequiresUnreferencedCode]/[RequiresDynamicCode] to BaseComponent.Dispose()/Refresh() in this batch, even though they call the newly-annotated InvokeMethodAsync.
    • Why: BaseComponent is the shared base class for every Sf* component in the library. Annotating it would cascade Requires* through effectively the entire public surface of the toolkit (every derived component's Dispose/interop calls), which is a major, deliberate architectural decision that deserves its own reviewed batch, not a one-line addition.
    • Alternative considered: Add the attribute immediately to silence the new warnings — rejected because it would either cascade uncontrolled (matching the honesty principle but changing the library's practical AOT story) or be added without full propagation (dishonest / inconsistent), which principle Changed the layout of SB demo. #4 rules out.

🔍 Quality Checklist

  • Code follows project conventions
  • All tests pass
  • No security vulnerabilities introduced
  • Documentation updated (if applicable)
  • Breaking changes documented (if any)

⚠️ Human Review Required

Please pay special attention to:

  • Decide the intended AOT/trim boundary for BaseComponent (JSInterop base class): either (a) accept that Requires* propagates through every derived Sf* component's public API (the technically honest outcome, since JsonSerializer.Serialize(object[]) on interop args is genuinely reflection-based), or (b) invest in a source-gen-based interop argument serialization path to avoid the cascade. This is the single biggest open design decision blocking further IL2026/IL3050 cleanup.
  • BL0005 warnings (78 unique, e.g. SfDataBoundComponent.Json, ChartAxisContainer.Name/Width/etc., SfChart.Margin.Top/Right/Bottom/Left) indicate [Parameter] properties being set from outside their own component (often from a sibling internal class the component owns, e.g. DataManager setting Json). Needs a case-by-case call on whether to restructure ownership or use CascadingParameter/internal setter patterns.
  • BL0007 warnings (264 unique, concentrated in Components/Charts — ChartAxis, LegendSettings, ChartSeries, ChartTrendline, ChartCommonMarker, etc.) flag [Parameter] properties with custom get/set logic instead of plain auto-properties, which can break Blazor's parameter change-detection diffing. Mechanical but high-volume; needs a decision on whether custom setters can be replaced with OnParametersSet-based logic.
  • IL2067/IL2070/IL2072/IL2075/IL2090/IL2091 (~230 unique DynamicallyAccessedMembers mismatches) are concentrated in Data/DataUtil.cs, Data/EnumerableExtensions.cs, Data/BaseComponent.cs, Base/Reflection/ReflectionExtension.cs, Data/NullableHelper.cs, and the Charts renderer tree (SeriesContainer, ChartSeriesRenderer). These need precise [DynamicallyAccessedMembers(...)] annotations on generic type parameters, not Requires* suppression.

📸 Evidence & Outputs

Test Logs

dotnet build (Release, net9.0) after adding RequiresUnreferencedCode/RequiresDynamicCode annotations

dotnet build Syncfusion.Blazor.Toolkit.csproj -c Release -f net9.0 (exit 0) [877 bytes]
Build succeeded.
    2424 Warning(s)
    0 Error(s)

Notable warnings now surfaced (expected, by design):
- IL2026/IL3050 warnings correctly appear at every genuine dynamic-query / reflection call site
  across Data/QueryableExtensions.cs, DynamicQueryableExtensions.cs, EnumerableExtensions.cs,
  EnumerableOperation.cs, QueryableOperation.cs, DataOperations.cs, DataUtil.cs,
  DynamicObjectOperations.cs, Adaptors/AdaptorBase.cs, Adaptors/BlazorAdaptor.cs, DataManager.cs,
  and Base/Reflection/FastReflectionExtension.cs.
- No new IL2046/IL3051 (Requires* attribute mismatch between interface/base and override) were
  introduced. The single pre-existing IL2046 in Components/Inputs/TextBox/SfTextBox.razor.LifeCycle.cs
  is unrelated to this change (file not touched) and existed prior to this fix.
- 0 compile errors; WarningsAsErrors list (CA-security rules) unaffected.


View Full Session • Generated by Agent

CodeStudio Agent added 3 commits September 22, 2026 03:34
…../1.0.0/net8.0/.toolversion.json, .../1.0.0/net9.0/.toolversion.json, package-lock.json, src/Base/SfBaseUtils.cs
… Base/Reflection helpers

Propagates [RequiresUnreferencedCode]/[RequiresDynamicCode] attributes from the
actual reflection/dynamic-code call sites (MethodInfo.MakeGenericMethod,
Type.MakeGenericType, Expression.Property/PropertyOrField(Expression,string),
Expression.Call(Type,string,Type[],Expression[])) up through the call graph to
the correct architectural boundaries:

- Base/Reflection/FastReflectionExtension.cs: CreateAccessor(...) overloads
  (Type.MakeGenericType).
- Data/QueryableExtensions.cs and DynamicQueryableExtensions.cs: the ~90 public
  string-property-name based LINQ helpers (OrderBy/OrderByDescending/ThenBy/
  ThenByDescending/Select/Skip/Take/Where/Predicate/Sum/Average/Max/Min/
  GroupByMany/Equal/NotEqual/GreaterThan*/LessThan*) and their private helpers
  (GetValueExpression, GetExpression, GetLambdaWithComplexPropertyNullCheck,
  the private Predicate overload, CreateGeneric, EnumerableSumMethods/
  EnumerableAverageMethods).
- Data/EnumerableExtensions.cs: Average/Sum/Max/Min<TSource> (Int16 LINQ
  provider methods) and InvokeParallel/GetParallelQuery.
- Data/EnumerableOperation.cs, QueryableOperation.cs, DataOperations.cs,
  DynamicObjectOperations.cs, DataUtil.cs: PerformSorting/PerformFiltering/
  PerformSearching/PerformGrouping/PerformSelect/PredicateBuilder/
  PerformAggregation/CastList/GroupSorting and their public wrappers.
- Data/Adaptors/AdaptorBase.cs (IAdaptor.PerformDataOperation<T> + virtual
  impl) and BlazorAdaptor.cs (PerformDataOperation<T> override,
  DataOperationInvoke<T>, CollectChildRecords): kept interface/base/override
  Requires* annotations consistent (verified via build: no new IL2046/IL3051).
- Data/DataManager.cs: ExecuteQuery/ExecuteQueryAsync/ProcessOfflineAsync,
  which are the public entry points of the dynamic-query engine and the
  correct stopping point for propagation within Data/ (Components/ callers
  are out of scope and will now surface their own IL2026/IL3050 warnings,
  which is expected/by design).

Verified via : 0 errors, no new
Requires*-attribute mismatches (IL2046/IL3051) introduced; the single
pre-existing IL2046 in Components/Inputs/TextBox is untouched and unrelated.
Propagates [RequiresUnreferencedCode]/[RequiresDynamicCode] from the actual
JsonSerializer.Serialize(object/object[], ...) call sites up to the correct
architectural boundary within Data/:

- Data/HttpHandler.cs: PrepareRequest(RequestOptions) and
  PrepareBatchRequest(RequestOptions, Type?), both of which serialize
  request payloads of statically-unknown shape.
- Data/JSInteropAdaptor.cs: SyncfusionInterop.InvokeMethodAsync<T>, which
  serializes an arbitrary object[] of JS interop call arguments.

Verified via build (net9.0): PrepareRequest/PrepareBatchRequest callers in
DataManager.cs (ExecuteQuery<T>/ProcessOfflineAsync) already carry matching
Requires* attributes, so no new warnings surface there. InvokeMethodAsync is
called from Data/BaseComponent.cs (Dispose, Refresh, InvokeAsync helpers),
which is NOT yet annotated -- this surfaces 6 new IL2026/IL3050 warning
locations in BaseComponent.cs. This is expected: BaseComponent is the shared
base for every Sf* component in the library, so propagating Requires* further
requires a deliberate, larger follow-up (see PENDING tasks) rather than a
one-line annotation, since it will cascade into every derived component's
public API. Net effect of this commit alone: 17 warnings removed inside
HttpHandler.cs/JSInteropAdaptor.cs, 12 new ones surfaced in BaseComponent.cs
(net -5), with zero new IL2046/IL3051 Requires*-attribute mismatches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant