149 lines
7.5 KiB
Markdown
149 lines
7.5 KiB
Markdown
|
|
---
|
||
|
|
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.
|