Hi i have a database application which uses registered modules.
Modules are registered via MEF, I need to provide settings to these modules (whose settings are also stored in the database).
Each plugin will implement different interfaces (that do different things through different interfaces).
As of now, i currently have a separate plugin container for each interface type – whilst the below works, there is a lot of replication for each and every plugin type.
The modules and module setting table is in a sql database with the following field types
[Table_PluginModules]
PluginId => Guid, PK
Enabled => boolean
[Table_PluginModule_settings]
PluginId => Guid, PK(col1), FK(Table_PluginModules.PluginID)
SettingName => varchar, PK(col2)
SettingValue => varchar
The module interfaces are below
interface ICar
{
string Name;
Guid PluginID;
void Handbrake();
}
interface IBike
{
string Name;
Guid PluginID;
void Peddle();
}
Then i have the plugin managers for the ICar and IBike interfaces
class CarPluginsManager()
{
[ImportMany]
public ICar[] Plugins
void LoadPluginsCar()
{
var cat = new DirectoryCatalog(path, "*.dll");
var container = new CompositionContainer(cat);
container.ComposeParts(this);
foreach (ICar plugin in Plugins)
{
// load settings from database for this plugin
List<ModuleSetting> settings = ModelContext.PluginSettings( x => x.PluginId == plugin.Plugin).ToList();
foreach(ModuleSetting setting in settings)
{
// do something with the setting value that was retreived for this plugin
string settingName = setting.SettingName;
string settingValue = setting.SettingValue;
}
// check if previously registered modules are not in the above list and disable if necesasry in the database
}
}
ICar GetCarPlugin(Guid id)
{
foreach (var plugin in Plugins)
{
if(plugin.PluginID == id)
{
// check module is enabled in the modules database table, if so
return plugin;
// if module is disabled, return null...
}
}
return null;
}
}
class BikePluginsManager()
{
[ImportMany]
public IBike[] Plugins
void LoadPluginsCar()
{
var cat = new DirectoryCatalog(path, "*.dll");
var container = new CompositionContainer(cat);
container.ComposeParts(this);
foreach (ICar plugin in Plugins)
{
// load settings from database for this plugin
List<ModuleSetting> settings = ModelContext.PluginSettings( x => x.PluginId == plugin.Plugin).ToList();
foreach(ModuleSetting setting in settings)
{
// do something with the setting value that was retreived for this plugin
string settingName = setting.SettingName;
string settingValue = setting.SettingValue;
}
// check if previously registered modules are not in the above list and disable if necesasry in the database
}
}
IBike GetBikePlugin(Guid id)
{
foreach (var plugin in Plugins)
{
if(plugin.PluginID == id)
// check module is enabled in the modules database table, if so
return plugin;
// if module is disabled, return null...
}
return null;
}
}
Other aspects of the business logic code will reference different modules to do different things.
When modules are loaded, i check if any previously registered modules are missing (in case DLLs were removed from the plugin directory) and disable the plugin in the plugin table.
These modules need to be ‘registered’ in the database so we can reference these modules as foreign keys (users can select different output modules), I am currently doing this through a table called plugins (defined at the top of this post) and an example actions table below:
[Table_Some_Action]
ActionID => int, PK
ModuleID => Guid, FK(Table_Modules)
So, I would like to make this common/generic, however i still need to be able to reference interfaces by interface type. What i am thinking of is doing something along the lines of:
interface IPlugin
{
string Name;
Guid PluginID;
}
IPlugInterfaceCar : IPlugin
{
void ApplyHandbrake();
}
IPlugInterfaceBike : IPlugin
{
void Peddle();
}
class CarPluginBinary : IPlugInterfaceCar
{
void ApplyHandbrake() {}
}
class BikePluginBinary : IPlugInterfaceBike
{
void ApplyHandbrake() {}
}
The problem is how do i handle the different types when the plugin manager class that composes the parts expects a single interface (or infact, we wont know these interfaces in advance, as they are loaded at runtime).
Any direction on this design strategy, including this reference to the modules from a database, would be very much appreciated.
Thanks,
Chris
I have written the following modules and linked the plugin module as an attribute. Unfortunatly it is a string as i dont think its possible to have GUID literals, however it does the job.
I think it is more flexible than the code before without having to resort to a single base interface type, and I can now link to the modules in the database via an FK, whenever the code willinstantiate a module the ID and interface are checked to ensure they are both valid.
Comments appreciated if you spot any other problems or improvements.
Plugin manager
Export attribute
Marking module as a plugin, place this compiled DLL in the plugin directory