c# - 当程序集在不同的文件夹中时,“组件没有由 URI 标识的资源”
问题描述
我正在使用 Prism + Unity 库构建模块化 WPF 应用程序。我将我的解决方案拆分为以下项目: - 通用项目(接口) - 加载 dll 的主应用程序 - 模块,它们不被主应用程序项目引用。
子模块 dll 构建到 bin\(Debug|Release)\Modules 文件夹
主应用程序中要加载的代码如下:
它:
- 从 Modules 文件夹加载 dll
- 在 DI 容器中注册类型
从加载的 dll 设置模块(搜索 IModule 类型)
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { base.ConfigureModuleCatalog(moduleCatalog); List<Assembly> allAssemblies = new List<Assembly>(); string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); foreach (string dll in Directory.GetFiles(path + "/Modules/", "*.dll")) allAssemblies.Add(Assembly.LoadFile(dll)); AutoRegisterClasses(allAssemblies); var modules = AllClasses.FromAssemblies(allAssemblies).Where(t => t.GetInterfaces().Contains(typeof(IModule))); foreach (var module in modules) { moduleCatalog.AddModule( new ModuleInfo() { ModuleName = module.Name, ModuleType = module.AssemblyQualifiedName, Ref = "file://" + module.Assembly.Location }); } } private void AutoRegisterClasses(List<Assembly> allAssemblies) { var defaultRegisters = AllClasses.FromAssemblies(allAssemblies).Where(t => t.IsDefined(typeof(AutoRegisterAttribute), true)); foreach (var register in defaultRegisters) foreach (var interface_ in register.GetInterfaces()) Container.GetContainer().RegisterType(interface_, register, null, new TransientLifetimeManager()); }
注册和模块初始化工作 - 一切都被调用。当我尝试解决子模块项目中实现的公共项目的交互时,我收到了正确的对象。但是,当我尝试显示此提供程序返回的ContentControl时,我收到以下异常:
组件“AdditionalDll.MainWindow”没有由 URI“/AdditionalDll;component/mainwindow.xaml”标识的资源。
我不知道为什么会出现问题,更有趣的是,当我不在 Modules 文件夹中存储 DLL 时 - 也就是说,当我将它们与主 exe 并排存储时,一切正常!
我创建了具有此问题的最小解决方案: https ://github.com/BAndysc/AssemblyLoadProblemExample
Common/Interfaces.cs - is common project, contains AutoRegister attribute and example interface that I will try to resolve
AdditionalDll/ExampleProvider.cs - is in submodule project, it is implementation of the mentioned interface, it returns new view from AdditionalDll project
TestMultiProjectApp/App.xaml.cs - setups everything - load dlls, register types, init modules
TestMultiProjectApp/MainWindow.xaml - is simple view, which shows given ContentControl
TestMultiProjectApp/MainWindowViewModel.cs - is simple viewmodel, which resolves IProvider interface (it works) and on command assigns returned view to property, which is then displayed by view (this causes exception)
虽然将 dll 与 exe 保存在同一文件夹中是一些解决方法,但我宁愿将它们保存在单独的文件夹中以保持整洁,如果有人知道如何修复它们,我将不胜感激。
解决方案
这在很多层面上都是有缺陷的。例如:
- 模块目录未找到该模块
- 应用程序想要手动加载模块中的类型
MainWindowViewModel
尝试创建和存储ContentControl
- 命名 - 为什么有两个
MainWindow
s?
你应该退后一步,重新审视你的架构。您的模块的职责是什么?您的应用程序的责任是什么?您想如何识别和加载模块?你想如何部署和加载模块的依赖关系?您的 gui 的计划布局如何?
你应该退后一步,重新审视一些关于 WPF 开发的基本文档,即数据绑定、数据模板、资源字典、视图和视图模型之间的区别。
编辑:让我谈谈评论中提到的一些观点。
- 一个模块存在的理由是它注册自己的东西。所以是的,如果其他人为模块这样做是很糟糕的,因为你首先拿走了模块带来的所有东西。事实上,我认为模块甚至不应该公开任何
public
类型。 - 视图模型的属性要么是内置类型,要么是其他视图模型。视图和视图模型之间的映射通过
DataTemplate
s 或(在 Prism 的情况下)通过ViewModelLocator
. 如果你能提供帮助,视图模型永远不应该拥有、创建或知道视图类型。 - 按照惯例注册本身并没有什么问题,但要限制自己手头的模块。应用程序可以注册应用程序已知的类型,但模块中的类型应该由模块注册。
- Prism 提供了一种在模块之间创建依赖关系的方法,以定义加载模块时使用的部分排序。
推荐阅读
- reactjs - 如何将产品的订单详细信息发送到 mongodb?
- asp.net - 图片标题 - 资源应使用缓存清除,但 URL 与配置的模式不匹配
- azure - 备份 Windows 服务器 Azure VM 新 Azure 恢复服务 Vault 错误代码 BMSUserErrorContainerObjectNotFound
- python - 在 pyqt5 小部件中更新 matplotlib
- google-calendar-api - Google Calendar API - 修改以下所有实例(更新自我与会者响应状态)
- javascript - Javascript 拖放更改 div dragDrop
- ssh - 需要帮助查找用于沉浸式实验室 Going Places 的 SSH 令牌
- python - 通过 pandas 与 pyarrow 转换模式
- python - 如何转置 pyspark 数据帧?
- laravel - 带有 Laravel 和 Vue2 的 InertiaJS:无法重新定义属性:$inertia