public void UnloadPlugin(string pluginId) { PluginsLoadContexts.Remove(pluginId); // 移除其中的控制器 _pluginControllerManager.RemoveControllers(pluginId); }
/// <summary> /// 获取指定 pluginId 的启用插件 /// </summary> /// <param name="pluginId"></param> /// <returns>1.插件未启用返回null, 2.找不到此插件上下文返回null 3.找不到插件主dll返回null 4.插件主dll中找不到实现了IPlugin的Type返回null, 5.无法实例化插件返回null</returns> public IPlugin Plugin(string pluginId) { // 1.所有启用的插件 PluginId var pluginConfigModel = PluginConfigModelFactory.Create(); IList <string> enablePluginIds = pluginConfigModel.EnabledPlugins; // 插件未启用返回null if (!enablePluginIds.Contains(pluginId)) { return(null); } // 找不到此插件上下文返回null if (!PluginsLoadContexts.Any(pluginId)) { return(null); } // 2.找到插件对应的Context var context = PluginsLoadContexts.Get(pluginId); // 3.找插件 主 Assembly // Assembly.FullName: HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Assembly pluginMainAssembly = context.Assemblies.Where(m => m.FullName.StartsWith($"{pluginId}, Version=")).FirstOrDefault(); // 找不到插件主dll返回null if (pluginMainAssembly == null) { return(null); } // 4.从插件主Assembly中 找实现了 TPlugin 接口的 Type, 若有多个,只要一个 Type pluginType = pluginMainAssembly.ExportedTypes.Where(m => (m.BaseType == typeof(IPlugin) || m.GetInterfaces().Contains(typeof(IPlugin))) && !m.IsInterface && !m.IsAbstract ).FirstOrDefault(); // 插件主dll中找不到实现了IPlugin的Type返回null if (pluginType == null) { return(null); } // 5.实例化插件 Type //(TPlugin)Activator.CreateInstance(pluginType,); //try to resolve plugin as unregistered service //object instance = EngineContext.Current.ResolveUnregistered(pluginType); object instance = ResolveUnregistered(pluginType); //try to get typed instance IPlugin typedInstance = (IPlugin)instance; // 无法实例化插件返回null if (typedInstance == null) { return(null); } return(typedInstance); }
/// <summary> /// 加载插件程序集 /// </summary> /// <param name="pluginId"></param> public void LoadPlugin(string pluginId) { // 此插件的 加载上下文 var context = new CollectibleAssemblyLoadContext(); #region 加载插件主dll // 插件的主dll, 不包括插件项目引用的dll string pluginMainDllFilePath = Path.Combine(PluginPathProvider.PluginsRootPath(), pluginId, $"{pluginId}.dll"); Assembly pluginMainAssembly; using (var fs = new FileStream(pluginMainDllFilePath, FileMode.Open)) { // 使用此方法, 就不会导致dll被锁定 pluginMainAssembly = context.LoadFromStream(fs); // 加载其中的控制器 _pluginControllerManager.AddControllers(pluginMainAssembly); } #endregion // TODO:未测试 加载插件引用的dll: 方法二: //AssemblyName[] referenceAssemblyNames = pluginMainAssembly.GetReferencedAssemblies(); //foreach (var assemblyName in referenceAssemblyNames) //{ // context.LoadFromAssemblyName(assemblyName); //} // TODO: 跳过不需要加载的 dll, eg: ASP.NET Core Shared Framework, 主程序中已有dll string[] skipDlls = new string[] { "Core.dll", "Domain.dll", "Framework.dll", "Services.dll", "Repositories.dll", "PluginCore.dll" }; #region 加载插件引用的dll // 加载插件引用的dll // eg: xxx/Plugins/HelloWorld string pluginDirPath = Path.Combine(PluginPathProvider.PluginsRootPath(), pluginId); var pluginDir = new DirectoryInfo(pluginDirPath); // 插件引用的所有dll (排除 主dll 和 skipDlls ) // 注意: 主程序中已有dll 必须跳过, 应为这些默认Context中已经加载, 而如果插件Context再次加载, 则认为这两个是不同Assembly, 导致其中的Type转换失败 // 这里简单来说,意思就是当在一个自定义LoadContext中加载程序集的时候,如果找不到这个程序集,程序会自动去默认LoadContext中查找,如果默认LoadContext中都找不到,就会返回null。 // 这里我突然想到会不会是因为DemoPlugin1、DemoPlugin2以及主站点的AssemblyLoadContext都加载了Mystique.Core.dll程序集的缘故,虽然他们加载的是同一个程序集,但是因为LoadContext不同,所以系统认为它是2个程序集。 // 参考: https://www.cnblogs.com/lwqlun/p/12930713.html var allReferenceFileInfos = pluginDir.GetFiles("*.dll").Where(p => p.Name != $"{pluginId}.dll" && !skipDlls.Contains(p.Name)); foreach (FileInfo file in allReferenceFileInfos) { using (var sr = new StreamReader(file.OpenRead())) { context.LoadFromStream(sr.BaseStream); } } #endregion // 这个插件加载上下文 放入 集合中 PluginsLoadContexts.Add(pluginId, context); }
/// <summary> /// 实现了指定接口或类型 的启用插件 /// </summary> /// <typeparam name="TPlugin">可以是一个接口,一个抽象类,一个普通实现类, 只要实现了 <see cref="IPlugin"/>即可</typeparam> /// <returns></returns> public IEnumerable <TPlugin> EnablePlugins <TPlugin>() where TPlugin : IPlugin // BasePlugin { // TODO: 目前这里还有问题, 不应该写为 BasePlugin, 不利于扩展, 不利于插件开发者自己实现 Install , Uninstall等 // 1.所有启用的插件 PluginId var pluginConfigModel = PluginConfigModelFactory.Create(); IList <string> enablePluginIds = pluginConfigModel.EnabledPlugins; foreach (var pluginId in enablePluginIds) { if (PluginsLoadContexts.Any(pluginId)) { // 2.找到插件对应的Context var context = PluginsLoadContexts.Get(pluginId); // 3.找插件 主 Assembly // Assembly.FullName: HelloWorld, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null Assembly pluginMainAssembly = context.Assemblies.Where(m => m.FullName.StartsWith($"{pluginId}, Version=")).FirstOrDefault(); if (pluginMainAssembly == null) { continue; } // 4.从插件主Assembly中 找实现了 TPlugin 接口的 Type, 若有多个,只要一个 Type pluginType = pluginMainAssembly.ExportedTypes.Where(m => (m.BaseType == typeof(TPlugin) || m.GetInterfaces().Contains(typeof(TPlugin))) && !m.IsInterface && !m.IsAbstract ).FirstOrDefault(); if (pluginType == null) { continue; } // 5.实例化插件 Type //(TPlugin)Activator.CreateInstance(pluginType,); //try to resolve plugin as unregistered service //object instance = EngineContext.Current.ResolveUnregistered(pluginType); object instance = ResolveUnregistered(pluginType); //try to get typed instance TPlugin typedInstance = (TPlugin)instance; if (typedInstance == null) { continue; } yield return(typedInstance); } } }