我已經寫以下模塊和鏈接插件模塊作爲一個屬性。不幸的是,這是一個字符串,因爲我不認爲它可能有GUID文字,但是它完成了這項工作。
我認爲它比以前的代碼更靈活,不必訴諸於單一的基本接口類型,現在我可以通過FK鏈接到數據庫中的模塊,只要代碼將實例化模塊的ID和接口進行檢查以確保它們都是有效的。
如果您發現任何其他問題或改進,您可以隨時發表評論。
的插件管理器
public interface IPluginManagerService
{
void RegisterModules();
IExternalAccountsPlugin GetExternalAccountsPlugin(Guid id);
IRecoveryActionPlugin GetExternalRecoveryActionsPlugin(Guid id);
}
namespace MyNamespace.Services
{
public class PluginManagerService : ServiceBase, IPluginManagerService
{
public PluginManagerService(ILogger logger, IUnitOfWork unitOfWork)
: base(logger)
{
m_UnitOfWork = unitOfWork;
RegisterModules();
}
protected IUnitOfWork m_UnitOfWork;
object Lock = new object();
// do not make public so we can perform further checks via strongly typed accessor functions
// get a list of modules that implement contracts of type T
protected IEnumerable< Lazy<T, IPluginMetadata> > GetInterfaces<T>()
{
return m_Container.GetExports<T, IPluginMetadata>();
}
// do not make public so we can perform further checks via strongly typed accessor functions
// returns the plugin with the provided ID
protected T GetPlugin<T>(Guid id)
{
return m_Container.GetExports<T, IPluginMetadata>().Where(x => x.Metadata.PluginID == id.ToString().ToUpper()).Select(x => x.Value).FirstOrDefault();
}
public IExternalAccountsPlugin GetExternalAccountsPlugin(Guid id)
{
return GetPluginModule<IExternalAccountsPlugin>(id);
}
public IRecoveryActionPlugin GetExternalRecoveryActionsPlugin(Guid id)
{
return GetPluginModule<IRecoveryActionPlugin>(id);
}
/* return a list of all available externalAccounts plugins */
public IEnumerable<Guid> ListExternalAccountsPluginIDs()
{
List<Guid> guids = new List<Guid>();
foreach (string id in GetInterfaces<IExternalAccountsPlugin>().Select(x => x.Metadata.PluginID).ToList())
{
guids.Add(Guid.Parse(id));
}
return guids;
}
protected T GetPluginModule<T>(Guid id)
{
ExternalPlugin pluginInDb = m_UnitOfWork.ExternalPlugins.GetByID(id);
if (pluginInDb != null)
{
if (pluginInDb.Enabled == true)
{
T binaryPlugin = GetPlugin<T>(id);
if (binaryPlugin == null)
throw new KeyNotFoundException();
else
return binaryPlugin;
}
}
return default(T);
}
CompositionContainer m_Container;
public void RegisterModules()
{
lock (Lock)
{
var pluginContainer = new AggregateCatalog();
var directoryPath = Path.GetDirectoryName(Assembly.GetCallingAssembly().Location) + "\\Plugins\\";
var directoryCatalog = new DirectoryCatalog(directoryPath, "*.dll");
//pluginContainer.Dispose();
// directoryCatalog.Refresh();
LogMessage("Searching for modules in " + directoryCatalog + "...");
pluginContainer.Catalogs.Add(directoryCatalog);
m_Container = new CompositionContainer(pluginContainer);
// m_Container.ComposeParts(this); // not required, this will load dependencies dynamically onto our object
LinkModulesWithDatabase();
}
}
string FormatModuleMessage(string name, Guid id, Version version)
{
return name + ". ID: " + id + ". Version: " + version.ToString();
}
protected delegate string ModuleNameHelper<T>(T t);
protected delegate Version ModuleVersionHelper<T>(T t);
protected void SetVersionOfExternalPlugin(ref ExternalPlugin plugin, int major, int minor, int build, int revision)
{
plugin.MajorVersion = major;
plugin.MinorVersion = minor;
plugin.Build = build;
plugin.RevsionVersion = revision;
}
protected void LoadNewAndExisting<T>(IEnumerable<Lazy<T,IPluginMetadata>> foundModules, ref int added, ref int disabled, ref int upgraded, ref int existing, ModuleNameHelper<T> moduleNameHelper, ModuleVersionHelper<T> moduleVersionHelper)
{
List<Guid> foundModuleIDs = new List<Guid>();
foreach (Lazy<T,IPluginMetadata> moduleInAssembly in foundModules)
{
Guid moduleInAssemblyId = Guid.Parse(moduleInAssembly.Metadata.PluginID);
Version moduleInAssemblyVersion = moduleVersionHelper(moduleInAssembly.Value);
string moduleInAssemblyName = moduleNameHelper(moduleInAssembly.Value);
ExternalPlugin moduleInDb = m_UnitOfWork.ExternalPlugins.GetByID(moduleInAssemblyId); // see if we can find the registered module in the database
if (moduleInDb != null)
{
Version moduleInDbVersion = new Version(moduleInDb.MajorVersion, moduleInDb.MinorVersion, moduleInDb.Build, moduleInDb.RevsionVersion);
if (moduleInAssemblyVersion > moduleInDbVersion)
{
LogMessage("Found updated module (previous version " + moduleInDbVersion + "). Upgrading " + FormatModuleMessage(moduleInAssemblyName, moduleInAssemblyId, moduleInAssemblyVersion));
moduleInDb.Enabled = true;
SetVersionOfExternalPlugin(ref moduleInDb, moduleInAssemblyVersion.Major, moduleInAssemblyVersion.Minor, moduleInAssemblyVersion.Build, moduleInAssemblyVersion.Revision);
upgraded++;
}
else if (moduleInAssemblyVersion < moduleInDbVersion)
{
LogMessage("Found old module (expected version " + moduleInDbVersion +"). Disabling " + FormatModuleMessage(moduleInAssemblyName, moduleInAssemblyId, moduleInAssemblyVersion));
moduleInDb.Enabled = false;
disabled++;
}
else
{
LogMessage("Loaded existing module " + FormatModuleMessage(moduleInAssemblyName, moduleInAssemblyId, moduleInAssemblyVersion));
moduleInDb.Enabled = true;
existing++;
}
moduleInDb.UpdatedDateUTC = DateTime.Now;
m_UnitOfWork.ExternalPlugins.Update(moduleInDb);
}
else // could not find any module with the provided ID
{
ExternalPlugin newModule = new ExternalPlugin();
SetVersionOfExternalPlugin(ref newModule, moduleInAssemblyVersion.Major, moduleInAssemblyVersion.Minor, moduleInAssemblyVersion.Build, moduleInAssemblyVersion.Revision);
newModule.Enabled = true;
newModule.ExternalPluginID = moduleInAssemblyId;
newModule.UpdatedDateUTC = DateTime.UtcNow;
m_UnitOfWork.ExternalPlugins.Insert(newModule);
LogMessage("Loaded new module " + FormatModuleMessage(moduleInAssemblyName, moduleInAssemblyId, moduleInAssemblyVersion));
added++;
}
foundModuleIDs.Add(moduleInAssemblyId);
}
IEnumerable<Guid> missingModules = m_UnitOfWork.ExternalPlugins.Context.Select(x => x.ExternalPluginID).Except(foundModuleIDs).ToList();
foreach(Guid missingID in missingModules)
{
ExternalPlugin pluginToDisable = m_UnitOfWork.ExternalPlugins.GetByID(missingID);
LogMessage("Cannot find previously registered module in plugin directory. Disabling: " + FormatModuleMessage("Unknown", pluginToDisable.ExternalPluginID, new Version(pluginToDisable.MajorVersion, pluginToDisable.MinorVersion, pluginToDisable.RevsionVersion, pluginToDisable.Build)));
pluginToDisable.Enabled = false;
pluginToDisable.UpdatedDateUTC = DateTime.UtcNow;
m_UnitOfWork.ExternalPlugins.Update(pluginToDisable);
disabled++;
}
m_UnitOfWork.Save();
}
protected void LinkModulesWithDatabase()
{
int added = 0;
int existing = 0;
int upgraded = 0;
int disabled = 0;
LogMessage("Loading ExternalAccountsModule plugins (IExternalAccountsModule).");
ModuleNameHelper<IExternalAccountsPlugin> accountsModuleNameHelper = delegate (IExternalAccountsPlugin t) { return t.Name; };
ModuleVersionHelper<IExternalAccountsPlugin> accountsModuleVersionHelper = delegate (IExternalAccountsPlugin t) { return t.ModuleVersion; };
IEnumerable<Lazy<IExternalAccountsPlugin, IPluginMetadata>> accountsPlugins = GetInterfaces<IExternalAccountsPlugin>();
LoadNewAndExisting<IExternalAccountsPlugin>(accountsPlugins, ref added, ref disabled, ref upgraded, ref existing, accountsModuleNameHelper, accountsModuleVersionHelper);
LogMessage("Finished loading modules, total " + (added + existing + upgraded) + " modules enabled. New: " + added + " . Existing: " + existing + ". Upgraded: " + upgraded + ". Disabled: " + disabled + ".");
}
}
}
導出屬性
public interface IPluginMetadata
{
string PluginID { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class PluginExportAttribute : ExportAttribute, IPluginMetadata
{
public PluginExportAttribute(Type t, string guid)
: base(t)
{
PluginID = guid.ToUpper();
}
public string PluginID { get; set; }
}
標記模塊作爲一個插件,把這個編譯的DLL中的插件目錄
[PluginExport(typeof(IExternalAccountsPlugin),"BE112EA1-1AA1-4B92-934A-9EA8B90D622C")]
public class MyModule: IExternalAccountsPlugin
{
}
IPluginManagerService externalAccountsModuleService = instance.Resolve<IPluginManagerService>();
IExternalAccountsPlugin accountsPlugin = externalAccountsModuleService.GetExternalAccountsPlugin(Guid.Parse("BE112EA1-1AA1-4B92-934A-9EA8B90D622C"));
IEnumerable<Guid> pluginIDs = externalAccountsModuleService.ListExternalAccountsPluginIDs();