首页 > 解决方案 > 当程序集在不同的文件夹中时,“组件没有由 URI 标识的资源”

问题描述

我正在使用 Prism + Unity 库构建模块化 WPF 应用程序。我将我的解决方案拆分为以下项目: - 通用项目(接口) - 加载 dll 的主应用程序 - 模块,它们不被主应用程序项目引用。

子模块 dll 构建到 bin\(Debug|Release)\Modules 文件夹

主应用程序中要加载的代码如下:

它:

  1. 从 Modules 文件夹加载 dll
  2. 在 DI 容器中注册类型
  3. 从加载的 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 保存在同一文件夹中是一些解决方法,但我宁愿将它们保存在单独的文件夹中以保持整洁,如果有人知道如何修复它们,我将不胜感激。

标签: c#wpfdllprismassemblies

解决方案


这在很多层面上都是有缺陷的。例如:

  • 模块目录未找到该模块
  • 应用程序想要手动加载模块中的类型
  • MainWindowViewModel尝试创建和存储ContentControl
  • 命名 - 为什么有两个MainWindows?

你应该退后一步,重新审视你的架构。您的模块的职责是什么?您的应用程序的责任是什么?您想如何识别和加载模块?你想如何部署和加载模块的依赖关系?您的 gui 的计划布局如何?

你应该退后一步,重新审视一些关于 WPF 开发的基本文档,即数据绑定、数据模板、资源字典、视图和视图模型之间的区别。

编辑:让我谈谈评论中提到的一些观点。

  • 一个模块存在的理由是它注册自己的东西。所以是的,如果其他人为模块这样做是很糟糕的,因为你首先拿走了模块带来的所有东西。事实上,我认为模块甚至不应该公开任何public类型。
  • 视图模型的属性要么是内置类型,要么是其他视图模型。视图和视图模型之间的映射通过DataTemplates 或(在 Prism 的情况下)通过ViewModelLocator. 如果你能提供帮助,视图模型永远不应该拥有、创建或知道视图类型。
  • 按照惯例注册本身并没有什么问题,但要限制自己手头的模块。应用程序可以注册应用程序已知的类型,但模块中的类型应该由模块注册。
  • Prism 提供了一种在模块之间创建依赖关系的方法,以定义加载模块时使用的部分排序。

推荐阅读