Files
DP44/enriched-partialglm/Common/DTS.Common.Core/PluginLib.md

149 lines
7.5 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
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<T>` | `public static T GetPlugin<T>() 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<T>` | `public static T GetPlugin<T>(string configPluginSetting) where T : class` | Retrieves a specific plugin from multiple exports by matching `configPluginSetting` against the plugin's `ToString()` value. |
| `GetPlugins<T>` | `public static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class` | Returns all MEF exports of type `T` as lazy-initialized references. |
| `GetPluginList<T>` | `public List<Assembly> GetPluginList<T>() 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<T>()` (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<T>()` or `PluginManager.GetPlugins<T>()`
---
## 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<T>()` 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<T>(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<T>()` 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.