7.5 KiB
7.5 KiB
source_files, generated_at, model, schema_version, sha256
| source_files | generated_at | model | schema_version | sha256 | ||||
|---|---|---|---|---|---|---|---|---|
|
2026-04-16T02:06:04.645753+00:00 | Qwen/Qwen3-Coder-Next-FP8 | 1 | 698a84042cfeb418 |
PluginLib
Documentation: Plugin Configuration and Management Module
1. Purpose
This module provides infrastructure for loading, configuring, and resolving plugins using the Managed Extensibility Framework (MEF). It reads plugin folder paths from a custom configuration section (DTS.Common.Core.PluginLib.Config) in App.config, initializes MEF catalogs and containers over those directories, and exposes static methods to retrieve plugin instances by exported contract type. It supports both singleton and multiple-plugin resolution patterns, and includes custom assembly resolution logic to handle dependencies across plugin directories.
2. Public Interface
PluginConfigData
string[] PluginFolders
XML-serialized array of plugin folder paths, populated from<Folder>elements under the<DatPro.Core.PluginLib.Config>root inApp.config.
PluginConfig
const string DTSPlugins = "DTSPlugins"
Hardcoded configuration key name used to retrieve the base plugin namespace prefix fromAppSettings.string GetDTSPluginsSetting(string setting)
Concatenates the value of the"DTSPlugins"app setting (retrieved viaDTSConfig.GetAppSetting) with the providedsettingusing"."as separator.
Example: If"DTSPlugins"="DTS.Equipment", thenGetDTSPluginsSetting("Exporter")returns"DTS.Equipment.Exporter".
PluginManager
AggregateCatalog PluginCatalog { get; set; }
MEF catalog aggregating plugin assemblies from configured directories.CompositionContainer PluginContainer { get; set; }
MEF composition container built fromPluginCatalog.static T GetPlugin<T>() where T : class
Returns a single exported instance of typeT. Throws if zero or more than one export ofTexists.static T GetPlugin<T>(string configPluginSetting) where T : class
Returns a specific exported instance of typeT, selected by matchingitem.Value.ToString()toconfigPluginSetting.
Note: This relies on the plugin instance’sToString()returning the expected identifier.static IEnumerable<Lazy<T>> GetPlugins<T>() where T : class
Returns all exported instances of typeT.List<Assembly> GetPluginList<T>() where T : class
Returns a deduplicated list of assemblies containing MEF parts (plugin types), not necessarily those that exportT.
Implementation detail: Iterates overDirectoryCatalog.Parts, extracts theAssemblyfrom each part’s type, and returns distinct entries.static PluginManager GetPluginManager(string appPath)
Returns the singletonPluginManagerinstance. Initializes it once per process if not already created.
Note:appPathis validated (asDirectoryInfo) but not used beyond that in the current implementation.
PluginConfigSectionHandler
FilterHashKeyCollection HashKeys { get; }
Configuration property mapping to thePluginFolderselement in the custom config section. ContainsFilterHashElemententries.
FilterHashKeyCollection
FilterHashElement this[int idx] => (FilterHashElement)BaseGet(idx);
Indexer to accessFilterHashElementitems by zero-based index.
FilterHashElement
string Key { get; set; }
Required, key-only configuration property (e.g., unused in current logic).string Value { get; set; }
Optional string value, used inPluginManagerconstructor as the plugin directory path.
3. Invariants
- Singleton enforcement:
PluginManageris a singleton; only one instance exists per AppDomain, enforced vialock(THREAD_LOCK)inGetPluginManager. - Configuration dependency:
PluginManagerconstructor requires theDTS.Common.Core.PluginLib.Configsection to be present inApp.config; otherwise, it logs and throws. - Directory existence: Each
FilterHashElement.Valuemust point to an existing directory; otherwise, anIOExceptionis thrown during initialization. - Assembly loading constraints: Assemblies named with prefixes
"DTS.Common","C1", or"Xceed"are excluded from explicitAssembly.LoadFromcalls in the constructor (though they may still be loaded via MEF orAssemblyResolve). - MEF contract resolution:
GetPlugin<T>()expects exactly one export ofT; otherwise, MEF throws (not explicitly handled in code). - Plugin selection via
ToString():GetPlugin<T>(string)matches plugins byitem.Value.ToString() == configPluginSetting, which is fragile and undocumented in plugin contracts.
4. Dependencies
Imports / Uses
DTS.Common.Core.Config.DTSConfig— for reading config sections (GetSection) and app settings (GetAppSetting).DTS.Common.Utilities.Logging.APILogger— for logging errors and warnings.System.ComponentModel.Composition.*— MEF types (AggregateCatalog,CompositionContainer,DirectoryCatalog,ReflectionModelServices).System.Configuration— forConfigurationSection,ConfigurationElementCollection,ConfigurationElement.
Consumers (inferred)
- Any module requiring plugin discovery/resolution (e.g., equipment exporters, data processors) likely calls
PluginManager.GetPlugin<T>()orGetPlugins<T>(). - Configuration infrastructure (
DTSConfig) must register theDTS.Common.Core.PluginLib.Configsection handler (not shown here).
5. Gotchas
appPathparameter inGetPluginManager(string appPath)is unused after validation. The constructor usesDTSConfig.DTSConfigInit(appPath), but subsequent calls toGetPluginManagerignoreappPathentirely — only the first call’sappPathmatters.GetPlugin<T>(string)usesToString()for identification, which is unreliable and not part of MEF’s contract model. Plugins must overrideToString()to return the expected identifier — a hidden coupling.- Assembly filtering is hardcoded and incomplete: Exclusion of
"DTS.Common","C1","Xceed"is done via string prefix on filename, not strong name or version — may break if filenames change or include versioned suffixes. - Redundant assembly loading: The constructor calls
Assembly.LoadFromfor each.dllafter addingDirectoryCatalog(which loads assemblies lazily). This may cause duplicate loads or conflicts. GetPluginList<T>()ignores the generic constraintT— it returns all plugin assemblies, not just those exportingT.CurrentDomain_AssemblyResolveis defined but never subscribed. The event handler is unused — assembly resolution failures may occur if plugins depend on other plugins not in the GAC.- No cleanup/disposal logic:
PluginCatalogandPluginContainerare not disposed, risking resource leaks in long-running processes. - Thread-safety is partial: While
GetPluginManageris thread-safe,PluginCatalogandPluginContainerare mutable properties — external modification after initialization is possible (though discouraged by design). FilterHashElement.Keyis unused — present in config schema but never referenced in code.
Documentation generated from provided source files. No behavior inferred beyond explicit code.