当子目录中的 dll 时,MEF 找不到模块的视图

MEF can't find module's views when dll in subdirectory

我尝试使用 Prism 和 MEF 制作一个小应用程序以了解其工作原理。我陷入了一个相当令人沮丧的问题。 我想在我的基本应用程序目录中有一个 "Modules" 子目录,我将所有模块的 dll 复制为 post 构建事件。 这些模块是带有 View 和 ViewModel 的 MVVM 应用程序。

我的问题是:当我在主应用程序目录中复制模块的 dll 时,视图显示在 shell 中,但是当我的模块在子目录中时,没有显示。 我的模块及其部分已找到,但根据 fuslogvw 无法找到视图:

* Assembly Binder Log Entry (27/11/2015 @ 16:45:28) *

The operation failed.

Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll

Running under executable C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe

--- A detailed error log follows.

=== Pre-bind state information ===

LOG: DisplayName = ModuleB.resources, Version=1.0.0.0, Culture=en-US, PublicKeyToken=null

(Fully-specified)

LOG: Appbase = file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/

LOG: Initial PrivatePath = NULL

LOG: Dynamic Base = NULL

LOG: Cache Base = NULL

LOG: AppName = HelloWorld.vshost.exe

Calling assembly : ModuleB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.

===

LOG: This bind starts in LoadFrom load context.

WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().

LOG: Using application configuration file: C:\Users\mouarf\Downloads\Prism-Samples-Wpf-master\Prism-Samples-Wpf-master\HelloWorld\HelloWorld\bin\Debug\HelloWorld.vshost.exe.Config

LOG: Using host configuration file:

LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.

LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).

LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.DLL.

LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.DLL.

LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources.EXE.

LOG: Attempting download of new URL file:///C:/Users/mouarf/Prism/HelloWorld/bin/Debug/en-US/ModuleB.resources/ModuleB.resources.EXE.

LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.DLL.

LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.DLL.

LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources.EXE.

LOG: Attempting download of new URL file:///C:/USERS/Mouarf/PRISM/HELLOWORLD/BIN/DEBUG/MODULES/en-US/ModuleB.resources/ModuleB.resources.EXE.

LOG: All probing URLs attempted and failed.

我不知道为什么 MEF 在 "modules\en-US\" 中查找,我认为这可能是它找不到任何视图的原因,但我找不到其他方式的说明。

我的引导程序:

 public class Bootstrapper : MefBootstrapper
{
    protected override void ConfigureAggregateCatalog()
    {
        base.ConfigureAggregateCatalog();

        this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstrapper).Assembly));
        string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Modules");
        DirectoryCatalog catalog = new DirectoryCatalog(path, "*.dll");
        this.AggregateCatalog.Catalogs.Add(catalog);
    }

    protected override DependencyObject CreateShell()
    {
        return this.Container.GetExportedValue<MainWindow>();
    }

    protected override void InitializeShell()
    {
        base.InitializeShell();

        Application.Current.MainWindow = (MainWindow)this.Shell;
        Application.Current.MainWindow.Show();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
    }

    protected override IModuleCatalog CreateModuleCatalog()
    {
        return new ConfigurationModuleCatalog();
    }
}

我的模块:

[ModuleExport(typeof(ModuleAModule))]
public class ModuleAModule : IModule
{
    IRegionManager _regionManager;

    [ImportingConstructor]
    public ModuleAModule(IRegionManager regionManager)
    {
        _regionManager = regionManager;
    }

    public void Initialize()
    {
        _regionManager.RegisterViewWithRegion(RegionNames.RightRegion, typeof(ViewA));
    }
}

我的看法:

/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
[Export]
public partial class ViewA : UserControl
{
    public ViewA()
    {
        InitializeComponent();
    }
}

我的视图模型:

[Export]
public class ViewAViewModel : BindableBase
{
    private string _title = "Module A";
    public string Title
    {
        get { return _title; }
        set { SetProperty(ref _title, value); }
    } 
}

有人吗?

编辑:

以下是适合想看一看的解决方案:HelloWorldPrismMef

编辑 2 :

调查还在继续,我发现了一个非常好用的mefx!所以我的问题似乎是:

[Part] ModuleA.ModuleAModule from: DirectoryCatalog (Path="Modules")

[Primary Rejection]

[Export] ModuleA.ModuleAModule (ContractName="Prism.Modularity.IModule")

[Import] ModuleA.ModuleAModule..ctor (Parameter="regionManager", ContractName="Prism.Regions.IRegionManager")

[Exception] System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint contract name

ContractName Prism.Regions.IRegionManager RequiredTypeIdentity Prism.Regions.IRegionManager n'a été trouvée.

at System.ComponentModel.Composition.Hosting.ExportProvider.GetExports(ImportDefinition definition, AtomicComposition atomicComposition)

at Microsoft.ComponentModel.Composition.Diagnostics.CompositionInfo.AnalyzeImportDefinition(ExportProvider host, IEnumerable`1 availableParts, ImportDefinition id)

这是否意味着我需要导出 IRegionManager class?

您 posted 的日志是关于尝试加载资源 .dll,MEF 永远不会加载的东西(资源 .dlls 用于存储应用程序资源信息,就像国际化的字符串)。您应该查找未提及资源 .dlls.

的错误

此外,在我看来您正在尝试编辑 the Prism Library HelloWorld example from GitHub。这个特定示例与 ModuleA 紧密耦合(我的意思是 ModuleAHelloWorld[= 中用作项目依赖项102=]),据我所知,您不能简单地将 ModuleA.dll 从主文件夹移动到 modules 文件夹并期望它能正常工作。

我的建议是添加一个新项目,将其设置为输出到模块文件夹并查看是否加载(单独保留 ModuleA 项目)。或者您可以从 HelloWorld 项目中删除引用并使用 post 构建事件.

现在关于从目录加载模块,以我的拙见,你把它复杂化了。您只需要

AgregateCatalog.Catalogs.Add(new DirectoryCatalog(@".\Modules"));

或者假设您有一个约定,为模块文件名指定类似于 AppName.Modules.[ModuleNameHere].dll 的模式(例如:AppName.Modules.LoginModule.dll 您可以使用类似这样的方式加载模块

AgregateCatalog.Catalogs.Add(new DirectoryCatalog(@".\Modules", "AppName.Modules.*Module.dll"));

虽然这里似乎不是这种情况,但如果您尝试从被视为不可信的区域加载模块,默认的安全策略是忽略该模块。如果您尝试通过 Windows Share 等网络连接 运行 应用程序,就会发生这种情况。对于这种情况,您需要将这些说明添加到 App.config

<runtime>
  <loadFromRemoteSources enabled="true" />
</runtime>

这些应该添加到 <startup /> 部分之后。

希望对您有所帮助。

编辑:

Does that mean that I need to Export a IRegionManager class ?

不,那只是抱怨,因为 mefx 没有加载导出它的程序集(Prism.Wpf.dll 我认为它被调用)。

我个人认为 mefx.exe 很麻烦;我更喜欢 GUI version

现在关于您的代码,我查看了 GitHub 存储库并进行了一些更改,但没有那么多(与 ModuleC 的引用有一些问题,所以我必须删除并重新添加 Prism.Mef & company):

  1. 已从 基础设施 项目
  2. 中删除了 PostBuildEvent
  3. 更改了模块项目中的 PostBuildEvent。这需要一些解释:

    • 所有宏都附加了目录分隔符“\”,因此您无需添加它(我指的是 $(OutDir)\Modules => $(OutDir)模块).
    • COPY/XCOPY 要求目标路径以分隔符结尾,否则目标路径将被解释为目标目录( $(OutDir)Modules => *$ (OutDir)模块*).
    • 目标目录需要存在(所以第一个命令应该是 MKDIR)

    我还评论(以 REM 开头的行是评论)复制 .pdb 的命令,因为我认为不需要它并添加了 /z 标记为 XCOPY.

  4. 添加了 ILoggerFacade 作为依赖项以演示模块实际加载。如果您 运行 来自 Visual Studio 调试器的应用程序,您将在调试 window.

  5. 中看到一些消息
  6. App.config 中添加了 <loadFromRemoteSources enabled="true" /> => <configuration /> => <runtime /> 这样我就可以 运行 在存储项目的挂载分区上安装应用程序.

所有这些都在 PR 中。

至于为什么不自动显示区域的浏览量,我还不能说。我会在空闲时间继续调查,但你问 Brian Lagunas 可能会更好,因为他是 Prism.

的开发者之一

编辑 2:

当我查看 Brian Lagunas 的个人资料时,我看到他回答了这个 幸运地解决了这个问题。

还将添加一个 PRGitHub.