--- 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-16T11:46:11.806575+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "695fcda61b684601" --- # DTS.Common.Import.Persist Module Documentation ## 1. Purpose This module implements the persistence layer for an import system, responsible for saving various imported data entities (hardware, users, test setups, groups, sensors, etc.) to a database. It follows the Strategy pattern where each `SaveVariantBase` subclass handles a specific entity type, providing a unified interface (`IPersistImport`) for the import pipeline while supporting progress tracking, cancellation, and error reporting. --- ## 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 reporting, cancellation checking, and access to the import data object. ### IPersistCalculator / PersistCalculator ```csharp public interface IPersistCalculator { void AddDone(); void AddDone(double value); double ProgressValue { get; } void AddToTotal(double value); } public class PersistCalculator : IPersistCalculator { public double ProgressValue { get; } // Returns _done / _total public void AddDone(); public void AddDone(double value); public void AddToTotal(double value); // Throws ArgumentOutOfRangeException if value < 0 } ``` Tracks progress as a ratio of completed work (`_done`) to total work (`_total`). ### SaveServer ```csharp public class SaveServer : SaveVariantBase { public SaveServer(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save(); // Throws NotImplementedException } ``` Stub implementation — currently unimplemented. ### SaveGroupTemplates ```csharp public class SaveGroupTemplates : SaveVariantBase { public SaveGroupTemplates(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, Func isCancelled = null) public override void Save(); // Iterates _importObject.GroupTemplates(), updates progress } ``` Iterates group templates without persisting them (appears to be a placeholder that only updates progress). ### SaveGlobalSettings ```csharp public class SaveGlobalSettings : SaveVariantBase { public override void Save(); // Calls SettingsDB.SetGlobalValue(g.Key, g.Value) for each setting } ``` Persists global settings via `SettingsDB.SetGlobalValue()`. ### SaveSensorModels ```csharp public class SaveSensorModels : SaveVariantBase { public User CurrentUser { get; set; } public override void Save(); // Commits sensor models via SensorModelCollection.SensorModelList.Commit() } ``` Persists sensor models. Requires `CurrentUser` to be set for the commit operation. ### SaveTestEngineerDetails ```csharp public class SaveTestEngineerDetails : SaveVariantBase { public override void Save(); // Adds test engineers via TestEngineerDetailsList.TestEngineerList.AddTestEngineer() } ``` Persists test engineer details. Sets status to `ImportExtraStatus.ReadingEngineerDetails`. ### SaveLabDetails ```csharp public class SaveLabDetails : SaveVariantBase { public override void Save(); // Adds labs via LabratoryDetailsList.AddLab() } ``` Persists lab details. Skips invalid/blank entries (calls `l.IsInvalidBlank()`). Sets status to `ImportExtraStatus.ReadingLabDetails`. ### SaveUsers ```csharp public class SaveUsers : SaveVariantBase { public IUIItems[] UIItems { get; set; } public User CurrentUser { get; set; } public override void Save(); // Commits users via UserCollection.UsersList.Commit() } ``` Persists users. **Asserts** that `CurrentUser.IsAdmin` is true (will fail in debug builds if non-admin attempts import). Sets status to `ImportExtraStatus.ReadingUsers`. ### SaveCustomerDetails ```csharp public class SaveCustomerDetails : SaveVariantBase { public override void Save(); // Adds customers via CustomerDetailsList.AddCustomer() } ``` Persists customer details. Skips invalid/blank entries. Note: Sets status to `ImportExtraStatus.ReadingLabDetails` (appears to be a copy-paste error). ### SaveHardware ```csharp public class SaveHardware : SaveVariantBase { public Dictionary OldDASIdToNewDASId { get; set; } = new Dictionary(); public Dictionary> TestIdToHardware { get; set; } = new Dictionary>(); public override void Save(); // Commits hardware via DASHardwareList.GetList().Commit() } ``` Persists hardware configurations. Tracks DAS ID remapping (`OldDASIdToNewDASId`) and hardware-to-test relationships (`TestIdToHardware`). Sorts hardware before processing. Progress includes both hardware items and their channels. ### 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; } = new Dictionary(); public SaveGroups(ImportObject importObject, IPersistCalculator persistCalculator, IImportNotification importNotification, SaveHardware saveHardware, Func isCancelled = null) public override void Save(); } ``` Persists static groups. Requires `SaveHardware` dependency for DAS ID remapping. Fixes missing zero method parameters and initial offsets. Maps old group IDs to new IDs. Reports warnings for groups with channels assigned to unknown DAS. ### SaveCustomChannels ```csharp public class SaveCustomChannels : SaveVariantBase { public ISO13499FileDb IsoDb { get; set; } public Dictionary CustomChannelTextIdToOldChannelId { get; set; } = new Dictionary(); public Dictionary CustomChannelOldChannelIdToChannel { get; set; } = new Dictionary(); public override void Save(); } ``` Persists custom channels via `CustomChannelList.List.Commit()`. Handles ID remapping for existing channels. Updates group templates and groups that reference custom channels. Calls `CustomChannelList.List.UpdateAll()` at the end. ### 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 via `TestTemplateList.TestTemplatesList.Commit()`. Requires dependencies on `SaveCustomChannels`, `SaveHardware`, and `SaveGroups`. Handles special case for `ImportFileFormat.SingleTestSetup` with custom naming. Deletes existing test setups before import. Updates hardware `TestId` references after commit. ### 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 test setups with specific configuration (checkout mode enabled, missing sensors allowed, etc.). Creates new groups from `ChannelsForGroup` configuration. ### SaveTestSetupHelper (Static Class) ```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); } ``` Helper methods extracted for reuse between `SaveTestSetup` and `SaveCheckoutTestSetup`. --- ## 3. Invariants 1. **Cancellation Check Pattern**: All `Save()` implementations check `IsCancelled()` at the start of each iteration loop and return immediately if true. 2. **Progress Reporting Pattern**: After each item is processed, implementations call `_persistCalculator.AddDone()` followed by `_importNotification.SetProgress(_persistCalculator.ProgressValue)`. 3. **Constructor Signature**: All `SaveVariantBase` subclasses accept the same base parameters: `ImportObject`, `IPersistCalculator`, `IImportNotification`, and optional `Func isCancelled`. 4. **Null Cancellation Handler**: If `isCancelled` is null, `SaveVariantBase` assigns a lambda returning `false`. 5. **ProgressValue Calculation**: `ProgressValue` is calculated as `_done / _total`. Division by zero is possible if `AddToTotal()` is never called (undefined behavior in source). 6. **AddToTotal Validation**: `PersistCalculator.AddToTotal()` throws `ArgumentOutOfRangeException` if `value < 0`. 7. **Hardware ID Mapping**: `SaveHardware` always populates `OldDASIdToNewDASId` when DAS IDs change during commit. 8. **Group ID Mapping**: `SaveGroups` always populates `OldGroupIdToNewGroupId` for non-embedded static groups. --- ## 4. Dependencies ### This module depends on: - **DTS.Common.Import.Interfaces** - `IPersistImport` interface (inferred) - **DTS.Common.Settings** - `SettingsDB` for global settings persistence - **DTS.SensorDB** - `SensorModelCollection`, `SensorsCollection` for sensor data - **DTS.Slice.Users** - `User`, `UserCollection`, `IUIItems` for user management - **DataPROWin7.DataModel** - Core data models (`TestTemplate`, `DASHardware`, `CustomerDetails`, `LabratoryDetails`, `TestEngineerDetails`, `CustomChannel`, etc.) - **DTS.Common.Import.Enums** - `ImportStatus`, `ImportExtraStatus`, `PossibleStatus`, `ImportFileFormat`, `ImportSeverityError` - **DTS.Common.Interface.Channels** - Channel interfaces - **DTS.Common.Interface.Groups.GroupList** - `IGroup`, `IGroupChannel`, `GroupHelper` - **DTS.Common.SharedResource.Strings** - `StringResources` for localized error messages - **DTS.Common.Storage** - `DbOperations` for database queries - **DTS.Common.ISO** - `ISO13499FileDb`, `IsoCodeStatics`, `MMEPossibleChannels` - **DTS.Common.Classes.Groups.ChannelSettings** - `ChannelSettingBase`, `IChannelSetting` - **DTS.Common.Interface.DASFactory.Diagnostics** - Diagnostics interfaces - **DTS.Common.Enums** - `SupportedExportFormatBitFlags` ### What depends on this module: - Cannot be determined from source alone (no consumers shown). --- ## 5. Gotchas 1. **SaveServer is unimplemented**: Calling `Save()` on `SaveServer` throws `NotImplementedException`. 2. **SaveCustomerDetails status typo**: Sets `ImportExtraStatus.ReadingLabDetails` instead of a customer-specific status (likely copy-paste error from `SaveLabDetails`). 3. **SaveUsers admin assertion**: Uses `Trace.Assert` to verify admin status — this only triggers in debug builds; production builds will silently proceed with potentially unauthorized operations. 4. **SaveGroupTemplates does nothing**: Iterates group templates but performs no persistence — only updates progress. 5. **Division by zero risk**: `PersistCalculator.ProgressValue` returns `_done / _total` without checking if `_total` is zero. 6. **SaveHardware sorts in-place**: Calls `_importObject.Hardware().ToList().Sort()` but then iterates the original `_importObject.Hardware()` — unclear if this has any effect since `ToList()` creates a copy. 7. **SaveCustomChannels memory concern**: Contains commented-out `GC.Collect()` with a code smell note referencing issue #11287 (out of memory exceptions on large CSV imports). 8. **Interdependent save order**: `SaveGroups` requires `SaveHardware` to be executed first (needs `OldDASIdToNewDASId`). `SaveTestSetup` requires `SaveCustomChannels`, `SaveHardware`, and `SaveGroups`. Order of execution matters. 9. **SaveCheckoutTestSetup clears groups**: Explicitly clears `t.Groups` and `t.ChannelsForGroup` before repopulating — this modifies the import object state. 10. **SaveTestSetup and SaveCheckoutTestSetup delete existing setups**: Both call `DeleteExistingTestSetups()` which removes existing test setups by name before importing.