当程序集位于不同文件夹中时,组件没有URI标识的资源。

时间:2018-10-30 00:49:08

标签: c# wpf dll prism assemblies

我正在使用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'标识的资源。

我不知道为什么会出现问题,更有趣的是,当我不将DLL存储在Modules文件夹中时-也就是说,当我将它们与主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放在同一文件夹中是一种解决方法,但我宁愿将它们放在单独的文件夹中以保持整洁,如果有人对如何修复它们有任何想法,我将不胜感激。

1 个答案:

答案 0 :(得分:0)

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

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

您应该退后一步,重新访问您的体系结构。您的模块的职责是什么?您的应用程序有什么责任?您如何识别并加载模块?您想如何部署和加载模块的依赖项? gui的计划布局如何?

然后您应该再退一步,并回顾一些有关WPF开发的基本文档,即数据绑定,数据模板,资源字典,视图和视图模型之间的区别。

编辑:让我谈谈评论中提到的几点。

  • 模块的根本原因是它注册了自己的东西。因此,是的,如果其他人为该模块执行此操作是不好的,因为您一开始就将模块带来的所有东西都带走了。实际上,我认为模块甚至不应该公开任何public类型。
  • 视图模型的属性内置在类型或其他视图模型中。视图和视图模型之间的映射通过DataTemplate发生,或者(对于Prism)通过ViewModelLocator发生。如果可以的话,视图模型永远都不应该拥有,创建或知道视图类型。
  • 按惯例注册本身没有任何问题,但仅限于手头的模块。应用程序可以注册该应用程序已知的类型,但是模块中的类型应该由模块注册。
  • Prism提供了一种在模块之间创建依赖关系的方法,以定义加载模块时使用的部分顺序。