Ejemplo n.º 1
0
        public void UnloadPlugin(string pluginId)
        {
            PluginsLoadContexts.Remove(pluginId);

            // 移除其中的控制器
            _pluginControllerManager.RemoveControllers(pluginId);
        }
Ejemplo n.º 2
0
        /// <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);
        }
Ejemplo n.º 3
0
        /// <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);
        }
Ejemplo n.º 4
0
        /// <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);
                }
            }
        }