/// <summary> /// 查找类型 /// </summary> /// <param name="assignTypeFrom">指定的类型</param> /// <param name="assemblies">程序集集合</param> /// <param name="onlyConcreteClasses">是否仅持续具体的实现类,不查询抽象类</param> /// <returns>结果</returns> public IEnumerable <Type> FindClassesOfType(Type assignTypeFrom, IEnumerable <Assembly> assemblies, bool onlyConcreteClasses = true) { var result = new List <Type>(); try { foreach (var a in assemblies) { Type[] types = null; try { types = a.GetTypes(); } catch { //Entity Framework 6 doesn't allow getting types (throws an exception) if (!ignoreReflectionErrors) { throw; } } if (types != null) { foreach (var t in types) { if (assignTypeFrom.IsAssignableFrom(t) || (assignTypeFrom.IsGenericTypeDefinition && DoesTypeImplementOpenGeneric(t, assignTypeFrom))) { if (!t.IsInterface) { if (onlyConcreteClasses) { if (t.IsClass && !t.IsAbstract) { result.Add(t); } } else { result.Add(t); } } } } } } } catch (ReflectionTypeLoadException ex) { var msg = string.Empty; foreach (var e in ex.LoaderExceptions) { msg += e.Message + Environment.NewLine; } var fail = new MapleException(msg, ex); Debug.WriteLine(fail.Message, fail); throw fail; } return(result); }
/// <summary> /// 初始化 /// </summary> /// <param name="applicationPartManager">Application part manager</param> /// <param name="config">Config</param> public static void Initialize(ApplicationPartManager applicationPartManager, MapleConfig config) { // ********************************************************************************************************************************* // // ApplicationPartManager 用途:剥离 Controller 到其他程序集中 // //MVC 框架启动的时候,首先会把 Assembly 程序集转换为 ApplicationPart 添加到 ApplicationPartManager 对象列表中,才能执行后续的任务, //因为要从这些程序集中查找 Controller,那么从这个特性我们可以延伸到, 利用此功能,我们可以从 Web 层剥离 Controller 到其他程序集中 // // ********************************************************************************************************************************* Check.NotNull(applicationPartManager, nameof(applicationPartManager)); Check.NotNull(config, nameof(config)); using (new WriteLockDisposable(Locker)) { // TODO: Add verbose exception handling / raising here since this is happening on app startup and could // prevent app from starting altogether var pluginFolder = new DirectoryInfo(CommonHelper.MapPath(PluginsPath)); //获取插件集合所在目录 _shadowCopyFolder = new DirectoryInfo(CommonHelper.MapPath(ShadowCopyPath)); //获取插件集合中Bin所在目录 //已加载的插件集合 var referencedPlugins = new List <PluginDescriptor>(); //不兼容或无法加载的插件集合 var incompatiblePlugins = new List <string>(); try { //从读取installedPlugins.json中读取已加载的插件清单 var installedPluginSystemNames = GetInstalledPluginNames(CommonHelper.MapPath(InstalledPluginsFilePath)); Debug.WriteLine("查询现有的插件,并将插件对应的DLL移至Bin目录中"); //确保 /Plugins 已经存在 Directory.CreateDirectory(pluginFolder.FullName); //确保 /Plugins/bin 已经存在 Directory.CreateDirectory(_shadowCopyFolder.FullName); //获取 /Plugins/bin 目录中所有的文件清单(包含子目录) var binFiles = _shadowCopyFolder.GetFiles("*", SearchOption.AllDirectories); if (config.ClearPluginShadowDirectoryOnStartup) { //否清理 /Plugins/bin 目录 foreach (var f in binFiles) { if (f.Name.Equals("placeholder.txt", StringComparison.InvariantCultureIgnoreCase)) { continue; } Debug.WriteLine("移除 " + f.Name); try { //ignore index.htm var fileName = Path.GetFileName(f.FullName); if (fileName.Equals("index.htm", StringComparison.InvariantCultureIgnoreCase)) { continue; } File.Delete(f.FullName); } catch (Exception exc) { Debug.WriteLine("移除文件 " + f.Name + " 失败. 错误描述: " + exc); } } } //加载查询的描述性信息 foreach (var dfd in GetDescriptionFilesAndDescriptors(pluginFolder)) { var descriptionFile = dfd.Key; //插件描述性信息所对应的文件 var pluginDescriptor = dfd.Value; //插件描述性信息实体类 //确保插件的版本有效 if (!pluginDescriptor.SupportedVersions.Contains(MapleVersion.CurrentVersion, StringComparer.InvariantCultureIgnoreCase)) { incompatiblePlugins.Add(pluginDescriptor.SystemName); continue; } //信息验证 if (string.IsNullOrWhiteSpace(pluginDescriptor.SystemName)) { throw new MapleException($"未设置插件 '{descriptionFile.FullName}' 名称. 请给插件定义唯一的名称再重新编译."); } if (referencedPlugins.Contains(pluginDescriptor)) { throw new MapleException($"插件 '{pluginDescriptor.SystemName}' 名称已经存在,请给插件定义唯一的名称再重新编译"); } //判断插件是否已经被安装 pluginDescriptor.Installed = installedPluginSystemNames .FirstOrDefault(x => x.Equals(pluginDescriptor.SystemName, StringComparison.InvariantCultureIgnoreCase)) != null; try { if (descriptionFile.Directory == null) { throw new MapleException($"Directory cannot be resolved for '{descriptionFile.Name}' description file"); } //确保插件所需的DLL在 plugins/bin 目录中存在,如果不存在则拷贝过去 var pluginFiles = descriptionFile.Directory.GetFiles("*.dll", SearchOption.AllDirectories) //just make sure we're not registering shadow copied plugins .Where(x => !binFiles.Select(q => q.FullName).Contains(x.FullName)) .Where(x => IsPackagePluginFolder(x.Directory)) .ToList(); //查询插件所对应的DLL文件 var mainPluginFile = pluginFiles .FirstOrDefault(x => x.Name.Equals(pluginDescriptor.AssemblyFileName, StringComparison.InvariantCultureIgnoreCase)); //如果插件所在文件不存在,则忽略 if (mainPluginFile == null) { incompatiblePlugins.Add(pluginDescriptor.SystemName); continue; } pluginDescriptor.OriginalAssemblyFile = mainPluginFile; //执行插件部署 pluginDescriptor.ReferencedAssembly = PerformFileDeploy(mainPluginFile, applicationPartManager, config); //拷贝插件所需但尚未加载的DLL foreach (var plugin in pluginFiles .Where(x => !x.Name.Equals(mainPluginFile.Name, StringComparison.InvariantCultureIgnoreCase)) .Where(x => !IsAlreadyLoaded(x))) { PerformFileDeploy(plugin, applicationPartManager, config); } //初始化插件类型 foreach (var t in pluginDescriptor.ReferencedAssembly.GetTypes()) { if (typeof(IPlugin).IsAssignableFrom(t)) { if (!t.IsInterface) { if (t.IsClass && !t.IsAbstract) { pluginDescriptor.PluginType = t; break; } } } } referencedPlugins.Add(pluginDescriptor); } catch (ReflectionTypeLoadException ex) { //插件加载失败,抛出异常 var msg = $"Plugin '{pluginDescriptor.FriendlyName}'. "; foreach (var e in ex.LoaderExceptions) { msg += e.Message + Environment.NewLine; } var fail = new Exception(msg, ex); throw fail; } catch (Exception ex) { //插件加载失败,抛出异常 var msg = $"Plugin '{pluginDescriptor.FriendlyName}'. {ex.Message}"; var fail = new MapleException(msg, ex); throw fail; } } } catch (Exception ex) { var msg = string.Empty; for (var e = ex; e != null; e = e.InnerException) { msg += e.Message + Environment.NewLine; } var fail = new MapleException(msg, ex); throw fail; } ReferencedPlugins = referencedPlugins; IncompatiblePlugins = incompatiblePlugins; } }