# 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) ```xml true true true true true false ``` ### 1.2 Update Host Project for AOT Publishing **File:** `NEW/src/JdeScoping.Host/JdeScoping.Host.csproj` Add: ```xml false true partial ``` --- ## 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` - `JdeScoping.Client/Models/FileUploadResult.cs` - `FileUploadResult` - `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): ```csharp .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): ```csharp 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` ```xml $(InterceptorsPreviewNamespaces);Dapper.AOT ``` ### 3.3 Enable AOT for Assemblies **`NEW/src/JdeScoping.DataAccess/DapperAotConfiguration.cs`** (new): ```csharp [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: ```csharp 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): ```csharp public sealed class TraversalResult { public int IterationsCompleted { get; init; } public int TotalWorkOrders { get; init; } } ``` **`NEW/src/JdeScoping.DataSync/Models/TableSyncStatusRow.cs`** (new): ```csharp 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`: **`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): ```csharp // Before .ValidateDataAnnotations() // After services.AddSingleton, DataSyncOptionsValidator>(); ``` **`NEW/src/JdeScoping.Infrastructure/DependencyInjection.cs`** (lines 33-35): Replace `GetSection().Get()` with `GetSection().GetValue()`. **`NEW/src/JdeScoping.Api/DependencyInjection.cs`** (lines 33-35): Replace `GetSection().Get()` 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): ```xml ``` ### 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 ```bash dotnet build NEW/JdeScoping.slnx ``` Should complete with no new errors. ### AOT Analysis ```bash dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishAot=true --no-build ``` Check for AOT compatibility warnings. ### Trim Analysis ```bash dotnet publish NEW/src/JdeScoping.Host -c Release -p:PublishTrimmed=true ``` Review trim warnings and verify TrimmerRoots coverage. ### Test Suite ```bash 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