Files
DP44/enriched-qwen3-coder-next/DataPRO/DataPRO.Core/PluginLib.md

102 lines
7.3 KiB
Markdown
Raw Normal View History

2026-04-17 14:55:32 -04:00
---
source_files:
- DataPRO/DataPRO.Core/PluginLib/PluginConfigData.cs
- DataPRO/DataPRO.Core/PluginLib/PluginConfig.cs
- DataPRO/DataPRO.Core/PluginLib/PluginConfigSectionHandler.cs
- DataPRO/DataPRO.Core/PluginLib/PluginManager.cs
generated_at: "2026-04-16T04:27:52.597409+00:00"
model: "Qwen/Qwen3-Coder-Next-FP8"
schema_version: 1
sha256: "a022d6fe08f41d94"
---
# PluginLib
## Documentation: Plugin Configuration and Management Module
---
### 1. Purpose
This module provides infrastructure for managing plugins in the DataPRO system using Managed Extensibility Framework (MEF). It defines configuration structures for specifying plugin directories, a static configuration accessor (`PluginConfig`), and a singleton `PluginManager` responsible for discovering, loading, and resolving plugin assemblies from configured directories. The module enables dynamic plugin discovery and composition at runtime based on MEF exports, while enforcing strict validation of plugin folder paths and thread-safe initialization.
---
### 2. Public Interface
#### `PluginConfigData`
- **`string[] PluginFolders`**
Public field annotated with `[XmlArrayItem("Folder")]`. Holds an array of strings representing plugin folder paths, deserialized from XML configuration.
#### `PluginConfig`
- **`const string DataProPlugins = "dataProPlugins"`**
Constant key used to locate the base plugin configuration setting in app.config.
- **`string GetDataProPluginsSetting(string setting)`**
Concatenates the value of the `"dataProPlugins"` app setting (retrieved via `DataProConfig.GetAppSetting`) with the provided `setting`, separated by a dot (`.`). Used to construct full setting names for plugin-specific configuration.
#### `PluginConfigSectionHandler`
- **`FilterHashKeyCollection HashKeys`**
Configuration property accessor for the `"PluginFolders"` element. Returns a `FilterHashKeyCollection` containing `FilterHashElement` instances parsed from the configuration section.
#### `FilterHashKeyCollection`
- **`FilterHashElement this[int idx]`**
Indexer to access `FilterHashElement` items by zero-based index.
- **`protected override ConfigurationElement CreateNewElement()`**
Returns a new `FilterHashElement` instance (used internally by .NET configuration system).
- **`protected override object GetElementKey(ConfigurationElement element)`**
Returns the `Key` property of the given `FilterHashElement` (used for internal collection management).
#### `FilterHashElement`
- **`string Key`**
Required, key property (marked `IsKey = true`). Represents the identifier/name for the configuration entry.
- **`string Value`**
Optional property. Stores the associated value (e.g., a file path).
#### `PluginManager`
- **`static T GetPlugin<T>() where T : class`**
Returns a *single* MEF-exported instance of type `T`. Throws an exception (implicitly, via MEF) if zero or more than one export of type `T` exists. Returns `null` if no export is found.
- **`static T GetPlugin<T>(string configPluginSetting) where T : class`**
Returns a *specific* MEF-exported instance of type `T` by matching `item.Value.ToString() == configPluginSetting`. Returns `null` if no matching plugin is found.
- **`static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class`**
Returns *all* MEF-exported instances of type `T` as `Lazy<T>` objects.
- **`List<Assembly> GetPluginList<T>() where T : class`**
Returns a deduplicated list of `Assembly` objects from directories in the MEF catalog that contain at least one part (plugin). *Note: This method returns after processing only the first `DirectoryCatalog` in the catalog list; subsequent catalogs are ignored.*
- **`static PluginManager GetPluginManager()`**
Thread-safe singleton accessor. Lazily initializes and returns the single `PluginManager` instance.
---
### 3. Invariants
- **Configuration Section Requirement**: The `"DataPro.Core.PluginLib.Config"` section *must* be present in the configuration file (`DataPro.config`). If absent, `PluginManager` constructor throws an `Exception`.
- **Plugin Directory Validation**: Every `FilterHashElement.Value` (interpreted as a plugin directory path) *must* point to an existing directory. If any directory does not exist, `PluginManager` constructor throws an `IOException`.
- **Thread Safety**: `PluginManager` is implemented as a singleton with lazy initialization protected by a `lock` on `ThreadLock`. All public static methods (`GetPlugin`, `GetPlugins`, `GetPluginManager`) are safe for concurrent use.
- **Assembly Loading Scope**: Assemblies are loaded *only* from directories specified in the configuration section. No fallback to other paths occurs during initialization.
- **MEF Composition Contract**: `GetPlugin<T>()` assumes exactly one export of type `T` exists; otherwise, MEF behavior (exception or `null`) applies. `GetPlugin<T>(string)` relies on `ToString()` of the exported instance for selection—this is fragile and not type-safe.
---
### 4. Dependencies
#### *This module depends on:*
- `System.Configuration` (for `ConfigurationSection`, `ConfigurationElement`, etc.)
- `System.ComponentModel.Composition` (for MEF types: `AggregateCatalog`, `CompositionContainer`, `DirectoryCatalog`, `Export`, `Lazy<T>`)
- `System.IO` (for `DirectoryInfo`, `FileInfo`, `Assembly.LoadFrom`)
- `System.Reflection` (for `Assembly`, `AssemblyName`)
- `DataPro.Core.Config` (specifically `DataProConfig.GetSection` and `DataProConfig.GetAppSetting`)
#### *This module is depended on by:*
- Any component requiring plugin resolution (e.g., via `PluginManager.GetPlugin<T>()`).
- Configuration infrastructure that consumes `"DataPro.Core.PluginLib.Config"` section.
---
### 5. Gotchas
- **`GetPluginList<T>()` is incomplete**: It returns after processing only the *first* `DirectoryCatalog` in `PluginCatalog.Catalogs`, ignoring all subsequent plugin directories. This is likely a bug.
- **`GetPlugin<T>(string)` uses `ToString()` for selection**: Matching plugins by `item.Value.ToString()` is unreliable and not robust—plugins may not override `ToString()` meaningfully, and this approach cannot distinguish between multiple instances of the same type.
- **Redundant assembly loading logic**: The constructor contains a loop that loads assemblies manually (`Assembly.LoadFrom`) *after* adding `DirectoryCatalog` to the `AggregateCatalog`. MEFs `DirectoryCatalog` already loads assemblies on-demand; this manual loading is unnecessary and may cause duplicate loads or version conflicts.
- **No error handling for assembly resolution**: The `CurrentDomain_AssemblyResolve` event handler is defined but *never subscribed* to `AppDomain.CurrentDomain.AssemblyResolve`. Dependency resolution failures will not be handled.
- **Path handling comment is misleading**: The commented-out `IsPathRooted` check includes a typo ("absolete" instead of "absolute") and is disabled—no validation of path absoluteness occurs at runtime.
- **`PluginConfigData` unused in runtime logic**: This class is defined but *not referenced anywhere* in the provided codebase. It appears to be a legacy or incomplete deserialization helper.
- **No cleanup/disposal**: `PluginManager` holds unmanaged resources (`CompositionContainer`, catalogs, loaded assemblies). No `IDisposable` implementation or finalizer is present—potential memory leaks if the app domain is long-lived and plugins are reloaded.