--- source_files: - Common/DTS.Common.Core/PluginLib/PluginConfigData.cs - Common/DTS.Common.Core/PluginLib/PluginConfig.cs - Common/DTS.Common.Core/PluginLib/PluginConfigSectionHandler.cs - Common/DTS.Common.Core/PluginLib/PluginManager.cs generated_at: "2026-04-16T11:42:06.536831+00:00" model: "zai-org/GLM-5-FP8" schema_version: 1 sha256: "698a84042cfeb418" --- # PluginLib Module Documentation ## 1. Purpose The `PluginLib` module provides a Managed Extensibility Framework (MEF)-based plugin infrastructure for the DTS system. It handles discovery, loading, and retrieval of plugins from configurable directories, supporting both single-plugin resolution and multiple implementations of the same interface. The module abstracts configuration management through custom configuration section handlers and provides thread-safe singleton access to the plugin catalog and container. --- ## 2. Public Interface ### `PluginManager` (Class) The primary entry point for plugin operations. | Method | Signature | Description | |--------|-----------|-------------| | `GetPluginManager` | `public static PluginManager GetPluginManager(string appPath)` | Returns the singleton instance of `PluginManager`. Initializes the plugin system on first call with the provided `appPath`. Thread-safe via lock on `THREAD_LOCK`. | | `GetPlugin` | `public static T GetPlugin() where T : class` | Retrieves a single MEF export of type `T`. Returns `null` if no export exists. Throws if multiple exports exist for the same type. | | `GetPlugin` | `public static T GetPlugin(string configPluginSetting) where T : class` | Retrieves a specific plugin from multiple exports by matching `configPluginSetting` against the plugin's `ToString()` value. | | `GetPlugins` | `public static IEnumerable> GetPlugins() where T : class` | Returns all MEF exports of type `T` as lazy-initialized references. | | `GetPluginList` | `public List GetPluginList() where T : class` | Returns a list of distinct `Assembly` objects from all loaded directory catalogs. Note: The generic type parameter `T` is not used in the implementation. | | Property | Type | Description | |----------|------|-------------| | `PluginCatalog` | `AggregateCatalog` | The MEF aggregate catalog containing all loaded plugin directories. | --- ### `PluginConfig` (Static Class) Provides configuration helper methods. | Member | Signature | Description | |--------|-----------|-------------| | `DTSPlugins` | `public const string DTSPlugins = "DTSPlugins"` | Constant for the app setting key name. | | `GetDTSPluginsSetting` | `public static string GetDTSPluginsSetting(string setting)` | Concatenates the app setting value for `DTSPlugins` with the provided `setting` parameter using `.` as separator. | --- ### `PluginConfigSectionHandler` (Class) Configuration section handler for `DTS.Common.Core.PluginLib.Config`. | Property | Type | Description | |----------|------|-------------| | `HashKeys` | `FilterHashKeyCollection` | Returns the collection of plugin folder elements from the configuration section. | --- ### `FilterHashKeyCollection` (Class) Collection class for configuration elements. | Member | Signature | Description | |--------|-----------|-------------| | `this[int idx]` | `public FilterHashElement this[int idx]` | Indexer to access elements by position. | --- ### `FilterHashElement` (Class) Represents a single plugin folder configuration entry. | Property | Type | Required | Description | |----------|------|----------|-------------| | `Key` | `string` | Yes | The key identifier for the element. | | `Value` | `string` | No | The plugin directory path. | --- ### `PluginConfigData` (Class) XML serialization support class for legacy configuration format. | Field | Type | Description | |-------|------|-------------| | `PluginFolders` | `string[]` | Array of plugin folder paths. Serialized with `XmlArrayItem("Folder")` attribute. | --- ## 3. Invariants 1. **Singleton Pattern**: `_pluginManager` is a static singleton; once initialized, subsequent calls to `GetPluginManager` return the existing instance regardless of the `appPath` parameter passed. 2. **Thread Safety**: All access to the singleton initialization is protected by `THREAD_LOCK` object. 3. **Configuration Requirement**: The configuration section `"DTS.Common.Core.PluginLib.Config"` must exist in the application configuration file, or the `PluginManager` constructor throws an `Exception`. 4. **Directory Existence**: All plugin directories specified in `FilterHashElement.Value` must exist at initialization time, or an `IOException` is thrown. 5. **Assembly Exclusion**: Assemblies with names starting with `"DTS.Common"`, `"C1"`, or `"Xceed"` are explicitly excluded from manual assembly loading in the constructor. 6. **MEF Export Uniqueness**: `GetPlugin()` (the parameterless overload) expects exactly zero or one export of type `T`; MEF throws an exception if multiple exports exist. --- ## 4. Dependencies ### This module depends on: - `DTS.Common.Core.Config` - For `DTSConfig.GetSection()`, `DTSConfig.GetAppSetting()`, and `DTSConfig.DTSConfigInit()` - `DTS.Common.Utilities.Logging` - For `APILogger.Log()` static method - `System.ComponentModel.Composition.Hosting` - MEF container and catalog types - `System.ComponentModel.Composition.ReflectionModel` - For `ReflectionModelServices.GetPartType()` - `System.Configuration` - For configuration section infrastructure - `System.Xml.Serialization` - For `PluginConfigData` XML serialization ### Consumers: - Any module requiring plugin extensibility via MEF exports - Code calling `PluginManager.GetPlugin()` or `PluginManager.GetPlugins()` --- ## 5. Gotchas 1. **Unused Assembly Resolve Handler**: The method `CurrentDomain_AssemblyResolve` is defined but **never wired up** to `AppDomain.CurrentDomain.AssemblyResolve`. It will never be called unless external code attaches it. 2. **Configuration Section Name Inconsistency**: `PluginConfigData` is decorated with `[XmlRoot(ElementName = "DatPro.Core.PluginLib.Config")]` (legacy "DatPro" naming), while `PluginManager` looks for `"DTS.Common.Core.PluginLib.Config"`. These are different section names; `PluginConfigData` appears to be legacy/unused code. 3. **Unused Parameter in GetPluginList**: The generic type parameter `T` in `GetPluginList()` is declared but never used in the method body. The method returns all assemblies regardless of type. 4. **Dead Code Before Lock**: In `GetPluginManager()`, a `DirectoryInfo` object is created from `appPath` before the lock but never used: ```csharp if (!string.IsNullOrWhiteSpace(appPath)) { var directoryInfo = new DirectoryInfo(appPath); } ``` 5. **Empty Loop Bodies**: The singleton check contains loops with empty bodies: ```csharp foreach (var catalog in _pluginManager.PluginCatalog.Catalogs) { var directoryCatalog = catalog as DirectoryCatalog; if (directoryCatalog == null) continue; } ``` This appears to be vestigial code with no effect. 6. **Plugin Matching via ToString()**: `GetPlugin(string configPluginSetting)` matches plugins by comparing `item.Value.ToString()` to `configPluginSetting`. This relies on the plugin's `ToString()` override returning a meaningful identifier. 7. **Null Return Possible**: `GetPlugin()` returns `null` if no export is found, not an exception. Callers must handle null. 8. **Hardcoded Assembly Exclusions**: The exclusion list (`"DTS.Common"`, `"C1"`, `"Xceed"`) is hardcoded in the constructor and not configurable.