--- source_files: - Common/DTS.Common.Import/Persist/SaveServer.cs - Common/DTS.Common.Import/Persist/SaveGroupTemplates.cs - Common/DTS.Common.Import/Persist/SaveVariantBase.cs - Common/DTS.Common.Import/Persist/SaveGlobalSettings.cs - Common/DTS.Common.Import/Persist/PersistCalculator.cs - Common/DTS.Common.Import/Persist/SaveSensorModels.cs - Common/DTS.Common.Import/Persist/SaveTestEngineerDetails.cs - Common/DTS.Common.Import/Persist/SaveLabDetails.cs - Common/DTS.Common.Import/Persist/SaveUsers.cs - Common/DTS.Common.Import/Persist/SaveCustomerDetails.cs - Common/DTS.Common.Import/Persist/SaveHardware.cs - Common/DTS.Common.Import/Persist/SaveTestSetupHelper.cs - Common/DTS.Common.Import/Persist/SaveGroups.cs - Common/DTS.Common.Import/Persist/SaveCustomChannels.cs - Common/DTS.Common.Import/Persist/SaveTestSetup.cs - Common/DTS.Common.Import/Persist/SaveCheckoutTestSetup.cs generated_at: "2026-04-17T15:28:30.641712+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "85e93f86bc467691" --- # DTS.Common.Import.Persist Module Documentation ## 1. Purpose This module implements the persistence layer for importing various data entities into the DTS (Data Test System). It provides a Strategy pattern implementation where each `SaveVariantBase` subclass handles persisting a specific entity type (hardware, users, test setups, groups, sensors, etc.) to the database. The module coordinates progress tracking, cancellation support, and ID remapping between old imported identifiers and newly assigned database identifiers. --- ## 2. Public Interface ### SaveVariantBase (Abstract Base Class) ```csharp public abstract class SaveVariantBase : IPersistImport { protected ImportObject _importObject; protected readonly IPersistCalculator _persistCalculator; protected readonly IImportNotification _importNotification; protected readonly Func IsCancelled; protected SaveVariantBase(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public abstract void Save(); } ``` Base class for all save variants. Provides shared infrastructure for progress notification and cancellation. --- ### PersistCalculator ```csharp public class PersistCalculator : IPersistCalculator { public double ProgressValue { get; } // Returns _done / _total public void AddDone() // Increments _done by 1 public void AddDone(double value) // Increments _done by value public void AddToTotal(double value) // Adds to _total (throws if value < 0) } ``` Calculates import progress as a ratio of completed work to total work. --- ### SaveHardware ```csharp public class SaveHardware : SaveVariantBase { public Dictionary OldDASIdToNewDASId { get; set; } public Dictionary> TestIdToHardware { get; set; } public SaveHardware(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists hardware entities. Tracks DAS ID remapping and hardware-to-test relationships for downstream consumers. --- ### SaveGroups ```csharp public class SaveGroups : SaveVariantBase { public bool CanCurrentUserCommitChannelCodes { get; set; } = true; public IChannelSetting DefaultZeroMethod { get; set; } public IChannelSetting DefaultZeroStart { get; set; } public IChannelSetting DefaultZeroEnd { get; set; } public IChannelSetting DefaultInitialOffset { get; set; } public Dictionary OldGroupIdToNewGroupId { get; set; } public SaveGroups(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, SaveHardware saveHardware, Func isCancelled = null) public override void Save() } ``` Persists static groups. Requires `SaveHardware` instance for DAS ID remapping. Updates sensor IDs and fixes missing zero method parameters. --- ### SaveCustomChannels ```csharp public class SaveCustomChannels : SaveVariantBase { public ISO13499FileDb IsoDb { get; set; } public Dictionary CustomChannelTextIdToOldChannelId { get; set; } public Dictionary CustomChannelOldChannelIdToChannel { get; set; } public SaveCustomChannels(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists custom channels with complex ID remapping logic. Maintains mappings for level trigger updates in test setups. --- ### SaveTestSetup ```csharp public class SaveTestSetup : SaveVariantBase { public User CurrentUser { get; set; } public string TestSetupName { get; set; } public SaveTestSetup(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, SaveCustomChannels saveCustomChannels, SaveHardware saveHardware, SaveGroups saveGroups, Func isCancelled = null) public override void Save() } ``` Persists test setups. Requires `SaveCustomChannels`, `SaveHardware`, and `SaveGroups` for cross-entity ID resolution. --- ### SaveCheckoutTestSetup ```csharp public class SaveCheckoutTestSetup : SaveVariantBase { public User CurrentUser { get; set; } public SaveCheckoutTestSetup(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, SaveCustomChannels saveCustomChannels, SaveHardware saveHardware, SaveGroups saveGroups, Func isCancelled = null, string setupName = null) public override void Save() } ``` Persists checkout-mode test setups with different default configuration than `SaveTestSetup`. --- ### SaveUsers ```csharp public class SaveUsers : SaveVariantBase { public IUIItems[] UIItems { get; set; } public User CurrentUser { get; set; } public SaveUsers(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists users. Requires `CurrentUser.IsAdmin` to be true (asserted via `Trace.Assert`). --- ### SaveSensorModels ```csharp public class SaveSensorModels : SaveVariantBase { public User CurrentUser { get; set; } public SaveSensorModels(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists sensor models via `SensorModelCollection.SensorModelList.Commit()`. --- ### SaveGlobalSettings ```csharp public class SaveGlobalSettings : SaveVariantBase { public SaveGlobalSettings(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists global settings via `SettingsDB.SetGlobalValue()`. --- ### SaveTestEngineerDetails ```csharp public class SaveTestEngineerDetails : SaveVariantBase { public SaveTestEngineerDetails(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists test engineer details via `TestEngineerDetailsList.TestEngineerList.AddTestEngineer()`. --- ### SaveLabDetails ```csharp public class SaveLabDetails : SaveVariantBase { public SaveLabDetails(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists laboratory details. Skips invalid/blank entries via `IsInvalidBlank()` check. --- ### SaveCustomerDetails ```csharp public class SaveCustomerDetails : SaveVariantBase { public SaveCustomerDetails(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Persists customer details. Skips invalid/blank entries via `IsInvalidBlank()` check. --- ### SaveGroupTemplates ```csharp public class SaveGroupTemplates : SaveVariantBase { public SaveGroupTemplates(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() } ``` Iterates group templates and updates progress. **Note: Does not appear to persist any data to database.** --- ### SaveServer ```csharp class SaveServer : SaveVariantBase { public SaveServer(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save() // Throws NotImplementedException } ``` Stub implementation. **Not implemented.** --- ### SaveTestSetupHelper (Static Helper) ```csharp public static class SaveTestSetupHelper { public static void AddHardwareFromEmbeddedGroups(TestTemplate t, SaveGroups saveGroups, List hardwareRemoved, List hardwareIncluded) public static void FixDasAff(Dictionary hardwareLookup, TestTemplate t) public static Dictionary PopulateHarwareLookup() public static void DeleteExistingTestSetups(IEnumerable testSetups, string userName) public static void UpdateLevelTriggers(ref Dictionary oldIdToNewIdLookup, SaveCustomChannels saveCustomChannels, IEnumerable testSetups) } ``` Shared utility methods for test setup persistence operations. --- ## 3. Invariants 1. **Progress Calculation**: `ProgressValue` is calculated as `_done / _total`. If `_total` is 0, this will result in division by zero (not guarded against). 2. **AddToTotal Validation**: `PersistCalculator.AddToTotal(double value)` throws `ArgumentOutOfRangeException` if `value < 0`. The exception message states "value should not be less or equal to zero" but the check is only for `< 0`, not `<= 0`. 3. **Cancellation Default**: If `isCancelled` parameter is null, `IsCancelled` defaults to `() => false`, meaning the operation will never be cancelled. 4. **SaveUsers Admin Requirement**: `SaveUsers.Save()` asserts `CurrentUser.IsAdmin` must be true via `Trace.Assert`. This is a debug-only assertion, not a runtime exception. 5. **SaveHardware ID Tracking**: `OldDASIdToNewDASId` only contains entries when `h.DASId != oldId` after commit. Unchanged IDs are not tracked. 6. **SaveGroups Dependency**: `SaveGroups` requires a `SaveHardware` instance in its constructor to access `OldDASIdToNewDASId` for channel DAS ID remapping. 7. **SaveTestSetup/SaveCheckoutTestSetup Dependencies**: Both require `SaveCustomChannels`, `SaveHardware`, and `SaveGroups` instances for cross-entity ID resolution. 8. **Hardware Sort Order**: `SaveHardware.Save()` calls `.Sort()` on the hardware list before processing, implying hardware must implement `IComparable`. --- ## 4. Dependencies ### External Dependencies (Inferred from Imports) | Module | Dependencies | |--------|--------------| | `SaveHardware` | `DataPROWin7.DataModel.Classes.Hardware`, `DTS.Common.Interface.DASFactory.Diagnostics`, `DTS.Common.SharedResource.Strings` | | `SaveGroups` | `DataPROWin7.DataModel.Classes.Hardware`, `DTS.Common.Classes.Groups.ChannelSettings`, `DTS.Common.Interface.Channels`, `DTS.Common.Interface.Groups.GroupList`, `DTS.Common.SharedResource.Strings`, `DTS.Common.Storage` | | `SaveCustomChannels` | `DataPROWin7.DataModel`, `DTS.Common.ISO` | | `SaveTestSetup`, `SaveCheckoutTestSetup` | `DataPROWin7.DataModel`, `DataPROWin7.DataModel.Classes.Hardware`, `DTS.SensorDB`, `DTS.Slice.Users` | | `SaveUsers` | `DTS.Slice.Users` | | `SaveSensorModels` | `DTS.SensorDB`, `DTS.Slice.Users` | | `SaveGlobalSettings` | `DTS.Common.Settings` | | `SaveTestEngineerDetails`, `SaveLabDetails`, `SaveCustomerDetails` | `DataPROWin7.DataModel`, `DTS.Common.Import.Enums` | | `SaveVariantBase` | `DTS.Common.Import.Interfaces` | ### Internal Dependencies - All save variants depend on `ImportObject`, `IPersistCalculator`, and `IImportNotification` - `SaveTestSetup`, `SaveCheckoutTestSetup` depend on `SaveCustomChannels`, `SaveHardware`, `SaveGroups` - `SaveGroups` depends on `SaveHardware` - `SaveTestSetupHelper` depends on `SaveGroups`, `SaveCustomChannels` --- ## 5. Gotchas 1. **SaveServer Not Implemented**: `SaveServer.Save()` throws `NotImplementedException`. This class should not be used in production. 2. **SaveGroupTemplates No Persistence**: `SaveGroupTemplates.Save()` iterates through group templates and updates progress but does not appear to commit any data to the database. 3. **Copy-Paste Error in SaveCustomerDetails**: The status is set to `ImportExtraStatus.ReadingLabDetails` instead of a customer-specific status. This appears to be a copy-paste error from `SaveLabDetails`. 4. **Division by Zero Risk**: `PersistCalculator.ProgressValue` does not guard against `_total` being zero, which would cause division by zero. 5. **Debug-Only Admin Check**: `SaveUsers` uses `Trace.Assert` for admin validation, which only fires in debug builds. In release builds, a non-admin user could attempt the import without exception. 6. **Invalid Details Tracking**: `SaveLabDetails` and `SaveCustomerDetails` set local `invalidLabDetails`/`invalidCustomerDetails` flags when encountering invalid entries, but these flags are never used or reported. 7. **Hardware List Mutation**: `SaveHardware.Save()` calls `.ToList().Sort()` on `_importObject.Hardware()`, which may have side effects on the underlying collection depending on implementation. 8. **Missing Error Reporting**: `SaveTestSetup.Save()` and `SaveCheckoutTestSetup.Save()` check `if (_importObject.Errors().Any())` but the body is empty with only a comment `//report errors`. 9. **Typo in Method Name**: `SaveTestSetupHelper.PopulateHarwareLookup()` is misspelled (should be "Hardware"). 10. **GC.Collect Commented Out**: In `SaveCustomChannels.Save()`, there is a commented-out `GC.Collect()` with a comment acknowledging it as a "critical code smell" (referencing bug #11287 for out-of-memory exceptions with large CSV files).