Files
jdescopingtool/aot_prep.md
T
2026-02-06 15:26:16 -05:00

16 KiB

AOT Readiness Implementation Plan

Overview

Make the JdeScopingTool .NET 10 solution ready for Native AOT compilation by addressing three critical areas:

  1. JSON Source Generation - Replace reflection-based System.Text.Json with source generators
  2. Dapper AOT - Enable compile-time SQL mapping generation
  3. Configuration Binding - Replace reflection-based options binding and validation

Current AOT Score: ~25/100 (1 of 14 projects ready) Target AOT Score: 90/100 (all projects AOT-compatible with known library exceptions)


Current State Summary

Projects by AOT Readiness

Status Project Key Blockers
Ready JdeScoping.Domain None - pure domain models
Needs Work JdeScoping.DataAccess Dapper, SqlKata, Oracle driver
Needs Work JdeScoping.DataSync Dapper + STJ without source gen
Needs Work JdeScoping.Core STJ + Configuration.Binder
Needs Work JdeScoping.Infrastructure STJ + SecureStore + LDAP
Needs Work JdeScoping.Api STJ + Swashbuckle + config binding
Needs Work JdeScoping.Host Depends on API/DataSync
Needs Work JdeScoping.Client STJ + Radzen + SignalR
Needs Work JdeScoping.ExcelIO ClosedXML
Needs Work JdeScoping.Database dbup-sqlserver
Needs Work ConfigManager.* STJ + System.CommandLine + Avalonia

No Existing AOT Configuration

  • No PublishAot property
  • No IsAotCompatible markers
  • No TrimMode settings
  • No [DynamicallyAccessedMembers] attributes
  • No [RequiresUnreferencedCode] attributes

Phase 1: Solution-Wide AOT Configuration

1.1 Create Directory.Build.props

File: NEW/src/Directory.Build.props (new)

<Project>
  <PropertyGroup>
    <IsTrimmable>true</IsTrimmable>
    <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
    <IsAotCompatible>true</IsAotCompatible>
    <EnableAotAnalyzer>true</EnableAotAnalyzer>
    <EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
    <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
  </PropertyGroup>
</Project>

1.2 Update Host Project for AOT Publishing

File: NEW/src/JdeScoping.Host/JdeScoping.Host.csproj

Add:

<PropertyGroup>
  <PublishAot Condition="'$(PublishAot)' == ''">false</PublishAot>
  <PublishTrimmed Condition="'$(PublishTrimmed)' == ''">true</PublishTrimmed>
  <TrimMode>partial</TrimMode>
</PropertyGroup>

Phase 2: JSON Source Generation (~45 types)

2.1 Create JsonSerializerContext Classes

Project Context File Types Count
JdeScoping.Core Json/CoreJsonContext.cs ~30 (ViewModels, Auth, Search)
JdeScoping.DataSync Json/DataSyncJsonContext.cs ~6 (ETL pipeline configs)
JdeScoping.Api Json/ApiJsonContext.cs ~10 (FileUploadResult, HealthCheck)
JdeScoping.Client Json/ClientJsonContext.cs ~15 (Client-side types)
ConfigManager.Core Json/ConfigManagerJsonContext.cs ~13 (ConfigModel sections)

2.2 Types Requiring [JsonSerializable] Attributes

Core Authentication & Identity Types

  • UserInfoDto - JdeScoping.Core/Models/Auth/UserInfoDto.cs
  • LoginResultModel - JdeScoping.Core/Models/Auth/LoginResultModel.cs
  • LoginModel - JdeScoping.Core/Models/Auth/LoginModel.cs

Search & Criteria Types

  • SearchCriteria - JdeScoping.Core/Models/Search/SearchCriteria.cs
  • Search - JdeScoping.Core/Models/Search/Search.cs
  • SearchViewModel - JdeScoping.Core/ViewModels/SearchViewModel.cs
  • SearchUpdateViewModel - JdeScoping.Core/ViewModels/SearchUpdateViewModel.cs
  • StatusUpdateViewModel - JdeScoping.Core/ViewModels/StatusUpdateViewModel.cs

File Upload & Result Types

  • FileUploadResult<T> - JdeScoping.Client/Models/FileUploadResult.cs
  • FileUploadResult<T> - JdeScoping.Api/Models/FileUploadResult.cs

Data Sync & Manual Sync Types

  • ManualSyncRequestViewModel - JdeScoping.Api/Contracts/ManualSync/ManualSyncRequestViewModel.cs
  • CreateManualSyncRequestDto - JdeScoping.Core/ViewModels/CreateManualSyncRequestDto.cs
  • PipelineInfoViewModel - JdeScoping.Api/Contracts/ManualSync/PipelineInfoViewModel.cs

ETL Pipeline Configuration Types

  • EtlPipelineConfig - JdeScoping.DataSync/Configuration/EtlPipelineConfig.cs
  • SourceElement - JdeScoping.DataSync/Configuration/SourceElement.cs
  • DestinationElement - JdeScoping.DataSync/Configuration/DestinationElement.cs
  • TransformElement - JdeScoping.DataSync/Configuration/TransformElement.cs
  • ScriptElement - JdeScoping.DataSync/Configuration/ScriptElement.cs
  • ParameterElement - JdeScoping.DataSync/Configuration/ParameterElement.cs

Configuration Model Types

  • ConfigModel - Utils/JdeScoping.ConfigManager.Core/Models/ConfigModel.cs
  • ConnectionStringsSection - Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringsSection.cs
  • ConnectionStringEntry - Utils/JdeScoping.ConfigManager.Core/Models/ConnectionStringEntry.cs

Data ViewModels

  • WorkOrderViewModel, ItemViewModel, WorkCenterViewModel, ProfitCenterViewModel
  • PartOperationViewModel, JdeUserViewModel, LotViewModel, ComponentLotViewModel, OperatorViewModel

2.3 Key Files to Create

NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs - Central context with:

  • All ViewModels (SearchViewModel, WorkOrderViewModel, ItemViewModel, etc.)
  • Auth models (UserInfoDto, LoginResultModel, LoginModel)
  • SearchCriteria, ApiError, ValidationError

NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs:

  • EtlPipelineConfig, SourceElement, DestinationElement, TransformElement

NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs:

  • FileUploadResult instantiations for each ViewModel type
  • HealthCheckResponse (replace anonymous type)

2.4 Registration Updates

NEW/src/JdeScoping.Api/DependencyInjection.cs (line ~80):

.AddJsonOptions(options =>
{
    options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, CoreJsonContext.Default);
    options.JsonSerializerOptions.TypeInfoResolverChain.Insert(0, ApiJsonContext.Default);
});

NEW/src/JdeScoping.Client/Services/ApiClientBase.cs (line ~16):

private static readonly JsonSerializerOptions JsonOptions = new()
{
    TypeInfoResolver = JsonTypeInfoResolver.Combine(
        ClientJsonContext.Default,
        CoreJsonContext.Default)
};

2.5 Fix Anonymous Type in Health Check

NEW/src/JdeScoping.Api/DependencyInjection.cs - Replace anonymous object with typed HealthCheckResponse class.


Phase 3: Dapper AOT (15+ model types)

3.1 Dapper Usage Locations

File Dapper Methods Model Types
LotFinderRepository.SearchManagement.cs QueryAsync, QueryFirstOrDefaultAsync Search
LotFinderRepository.Lookups.cs QueryAsync Item, Lot, WorkOrder, WorkCenter, ProfitCenter, JdeUser
LotFinderRepository.DataSync.cs QueryAsync DataUpdate
SearchProcessor.cs QueryAsync, QuerySingleOrDefaultAsync SearchResult, MisSearchResult, MisNonMatchSearchResult
ManualSyncRequestService.cs QueryAsync, QuerySingleAsync, ExecuteAsync ManualSyncRequest
WorkOrderTraversalService.cs QuerySingleAsync (int, int) tuple
DataUpdateRepository.cs QueryAsync, ExecuteScalarAsync, ExecuteAsync DataUpdate, (string, int, DateTime?, int) tuple
SearchRepository.cs QueryFirstOrDefaultAsync, ExecuteAsync Search

3.2 Add Dapper.AOT Package

Files: JdeScoping.DataAccess.csproj, JdeScoping.DataSync.csproj

<PackageReference Include="Dapper.AOT" Version="1.0.48" />
<PropertyGroup>
  <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);Dapper.AOT</InterceptorsPreviewNamespaces>
</PropertyGroup>

3.3 Enable AOT for Assemblies

NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs (new):

[assembly: DapperAot]

3.4 Fix Private Field Mapping Pattern

Change 6 models from private to internal for Dapper.AOT compatibility:

Model File
Item Core/Models/Inventory/Item.cs
Lot Core/Models/Inventory/Lot.cs
WorkOrder Core/Models/WorkOrders/WorkOrder.cs
WorkCenter Core/Models/Organization/WorkCenter.cs
ProfitCenter Core/Models/Organization/ProfitCenter.cs
JdeUser Core/Models/Organization/JdeUser.cs

Change:

private int LastUpdateDate { get; set; }  // Before
[Column("LastUpdateDate")]
internal int LastUpdateDate { get; set; }  // After

3.5 Replace Tuple Queries with Named Types

Create explicit result types:

NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs (new):

public sealed class TraversalResult
{
    public int IterationsCompleted { get; init; }
    public int TotalWorkOrders { get; init; }
}

NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs (new):

public sealed class TableSyncStatusRow
{
    public string TableName { get; init; } = string.Empty;
    public int UpdateType { get; init; }
    public DateTime? LastSuccessfulSync { get; init; }
    public int RecentFailures { get; init; }
}

Phase 4: Configuration Binding

4.1 Options Classes Inventory

Project Options Class Has DataAnnotations Uses ValidateDataAnnotations
JdeScoping.Core SecureStoreOptions No No
JdeScoping.Core RsaKeyOptions No No
JdeScoping.Infrastructure LdapOptions Yes (1 Range) No
JdeScoping.Api AuthOptions No No
JdeScoping.DataAccess DataAccessOptions No No
JdeScoping.DataAccess SearchProcessingOptions No No
JdeScoping.DataAccess SearchProcessingConfiguration No No
JdeScoping.DataSync DataSyncOptions Yes (6 Range) Yes
JdeScoping.DataSync WorkProcessorOptions Yes (3 Range) Yes
JdeScoping.ExcelIO ExcelExportOptions No No
JdeScoping.DataSync.Dev DevPipelineOptions No No

4.2 Create AOT-Compatible Validators

Replace ValidateDataAnnotations() with IValidateOptions<T>:

NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs (new) NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs (new)

4.3 Update DependencyInjection Files

NEW/src/JdeScoping.DataSync/DependencyInjection.cs (lines 27-36):

// Before
.ValidateDataAnnotations()

// After
services.AddSingleton<IValidateOptions<DataSyncOptions>, DataSyncOptionsValidator>();

NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs (lines 33-35): Replace GetSection().Get<T>() with GetSection().GetValue<T>().

NEW/src/JdeScoping.Api/DependencyInjection.cs (lines 33-35): Replace GetSection().Get<T>() with explicit property binding.

4.4 Remove Unused Package

NEW/src/JdeScoping.DataSync/JdeScoping.DataSync.csproj: Remove Microsoft.Extensions.Options.DataAnnotations reference.


Phase 5: Third-Party Library Handling

5.1 Package Risk Assessment

Risk Level Packages
🔴 High Dapper (mitigated by Dapper.AOT), SqlKata, Swashbuckle, ClosedXML, dbup-sqlserver
🟠 Medium protobuf-net-data, System.CommandLine, SecureStore
🟡 Low Microsoft.Data.SqlClient, Oracle driver, LDAP, Serilog
🟢 Safe Cronos, OneOf (has source generator), ZstdSharp

5.2 Create Trimmer Roots

NEW/src/JdeScoping.Host/TrimmerRoots.xml (new):

<linker>
  <assembly fullname="Dapper">
    <type fullname="Dapper.*" preserve="all" />
  </assembly>
  <assembly fullname="Oracle.ManagedDataAccess">
    <type fullname="Oracle.ManagedDataAccess.*" preserve="all" />
  </assembly>
  <assembly fullname="ClosedXML">
    <type fullname="ClosedXML.*" preserve="all" />
  </assembly>
  <assembly fullname="SqlKata">
    <type fullname="SqlKata.*" preserve="all" />
  </assembly>
</linker>

5.3 Known Limitations

These packages have partial/no AOT support - preserved via trimmer roots:

  • ClosedXML (Excel generation)
  • Oracle.ManagedDataAccess.Core
  • dbup-sqlserver (migrations - dev only)
  • Swashbuckle (Swagger - dev only)

Implementation Sequence

Step Task Files
1 Create Directory.Build.props 1 new
2 Create CoreJsonContext 1 new
3 Create DataSyncJsonContext 1 new
4 Create ApiJsonContext + HealthCheckResponse 1 new
5 Create ClientJsonContext 1 new
6 Create ConfigManagerJsonContext 1 new
7 Update Api DependencyInjection (JSON registration) 1 edit
8 Update ApiClientBase (JSON options) 1 edit
9 Update PipelineRegistry (JSON options) 1 edit
10 Add Dapper.AOT packages 2 edits
11 Create DapperAotConfiguration 1 new
12 Update 6 models (private→internal) 6 edits
13 Create TraversalResult, TableSyncStatusRow 2 new
14 Update tuple queries to use named types 2 edits
15 Create DataSyncOptionsValidator 1 new
16 Create WorkProcessorOptionsValidator 1 new
17 Update DataSync DependencyInjection 1 edit
18 Update Infrastructure DependencyInjection 1 edit
19 Update Api DependencyInjection (config) 1 edit
20 Update Host csproj (AOT settings) 1 edit
21 Create TrimmerRoots.xml 1 new
22 Remove DataAnnotations package 1 edit

Total: 14 new files, 18 edits


Verification

Build Verification

dotnet build NEW/JdeScoping.slnx

Should complete with no new errors.

AOT Analysis

dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishAot=true --no-build

Check for AOT compatibility warnings.

Trim Analysis

dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishTrimmed=true

Review trim warnings and verify TrimmerRoots coverage.

Test Suite

dotnet test NEW/JdeScoping.slnx

All existing tests should pass.

E2E Verification

Run Playwright tests to verify JSON serialization works end-to-end.


Critical Files Summary

New Files (14):

  • NEW/src/Directory.Build.props
  • NEW/src/JdeScoping.Core/Json/CoreJsonContext.cs
  • NEW/src/JdeScoping.DataSync/Json/DataSyncJsonContext.cs
  • NEW/src/JdeScoping.Api/Json/ApiJsonContext.cs
  • NEW/src/JdeScoping.Client/Json/ClientJsonContext.cs
  • NEW/src/Utils/JdeScoping.ConfigManager.Core/Json/ConfigManagerJsonContext.cs
  • NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs
  • NEW/src/JdeScoping.DataAccess/Models/TraversalResult.cs
  • NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs
  • NEW/src/JdeScoping.DataSync/Validation/DataSyncOptionsValidator.cs
  • NEW/src/JdeScoping.DataSync/Validation/WorkProcessorOptionsValidator.cs
  • NEW/src/JdeScoping.Host/TrimmerRoots.xml

Key Edits:

  • NEW/src/JdeScoping.Api/DependencyInjection.cs - JSON + config
  • NEW/src/JdeScoping.Client/Services/ApiClientBase.cs - JSON options
  • NEW/src/JdeScoping.DataSync/DependencyInjection.cs - Validators
  • NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs - Config binding
  • NEW/src/JdeScoping.Host/JdeScoping.Host.csproj - AOT settings
  • 6 model files for private→internal field change