Files
2026-04-17 14:55:32 -04:00

7.3 KiB
Raw Permalink Blame History

source_files, generated_at, model, schema_version, sha256
source_files generated_at model schema_version sha256
DataPRO/DataPRO.Core/PluginLib/PluginConfigData.cs
DataPRO/DataPRO.Core/PluginLib/PluginConfig.cs
DataPRO/DataPRO.Core/PluginLib/PluginConfigSectionHandler.cs
DataPRO/DataPRO.Core/PluginLib/PluginManager.cs
2026-04-16T04:27:52.597409+00:00 Qwen/Qwen3-Coder-Next-FP8 1 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.