无法解决MEF导入问题

时间:2010-04-19 22:07:05

标签: c# wpf inversion-of-control ioc-container mef

这是我之前的一篇文章的延续,其中涉及解析我的WPF应用程序中的模块。这个问题与模块的相互依赖性的影响以及构建这些模块的方法(即通过MEF或通过new)对MEF解决关系的能力有关。

我尝试了两种方法:

  • 左方法:应用程序实现IError
  • 正确的方法:App有一个实现IError的成员

左方法

我的代码背后看起来像这样(只是与MEF相关的东西):

// app.cs
[Export(typeof(IError))]
public partial class Window1 : Window, IError
{
    [Import]
    public CandyCo.Shared.LibraryInterfaces.IPlugin Plugin { get; set; }
    [Import]
    public CandyCo.Shared.LibraryInterfaces.ICandySettings Settings { get; set; }

    private ICandySettings Settings;

    public Window1()
    {
        // I create the preferences here with new, instead of using MEF.  I wonder
        // if that's my whole problem?  If I use MEF, and want to have parameters
        // going to the constructor, then do I have to [Export] a POCO (i.e. string)?
        Settings = new CandySettings( "Settings", @"c:\settings.xml");

        var catalog = new DirectoryCatalog( ".");
        var container = new CompositionContainer( catalog);
        try {
            container.ComposeParts( this);
        } catch( CompositionException ex) {
            foreach( CompositionError e in ex.Errors) {
                string description = e.Description;
                string details = e.Exception.Message;
            }
            throw;
        }
    }
}

// plugin.cs
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
    [Import]
    public CandyCo.Shared.LibraryInterfaces.ICandySettings CandySettings { get; set; }
    [Import]
    public CandyCo.Shared.LibraryInterfaces.IError ErrorInterface { get; set; }

    [ImportingConstructor]
    public Plugin( ICandySettings candy_settings, IError error_interface)
    {
        CandySettings = candy_settings;
        ErrorInterface = error_interface;
    }
}

// candysettings.cs
[Export(typeof(ICandySettings))]
public class CandySettings : ICandySettings
{
    ...
}

右侧方法

基本上与左侧方法相同,只是我在与Window1相同的程序集中创建了一个继承IError的类。然后我使用[导入]试图让MEF为我解决这个问题。

任何人都可以解释我在这里接触MEF的两种方式是否有缺陷?我已经在黑暗中呆了很长时间,而不是阅读MEF并尝试不同的建议,我已经在我的解决方案中添加了MEF并且正在进入代码。它看起来失败的部分是它调用partManager.GetSavedImport()时。出于某种原因,importCache为null,我不明白。到目前为止,它一直在查看部件(Window1)并尝试解析两个导入的接口 - IError和IPlugin。我原以为它会输入代码来查看同一个可执行文件夹中的其他程序集,然后检查它是否导出以便它知道如何解决导入......

我在代码中发现了一个错误,当我修复它时,MEF异常发生了变化,也更有用。它清楚地指出它无法找到CandySettings的默认构造函数!并且挖掘更多,我找到了good post from Glenn Block来讨论这个问题。所以我需要读完它,看看他的解决方法是否能解决问题。我仍然会感谢更多的答案,因为无法确定解决方法是否正确。

2 个答案:

答案 0 :(得分:1)

这篇文章确实有帮助。我之前没有见过这些信息,但它完全为我做了诀窍。

http://mindinthewater.blogspot.com/2010/01/using-mef-with-classes-which-take.html

基本上,我的问题是我需要将值传递给构造函数。我过去的所有测试都涉及将接口传递给其他共享库,但就我而言,我只想传递几个字符串。我显然不想尝试将这些字符串包装在接口中以传递POCO。

我绕过这种不便的第一次尝试是尽我所能使用默认构造函数。然后我把它留给命运,开发人员会记得调用Init()方法。由于显而易见的原因,这很糟糕,但无论如何我想尝试一下。最后,它只是不起作用 - 这里的问题是MEF想要解决导入和导出,但是在组成部件之后,我的Init()方法才会被调用。因此,该特定库的任何其他依赖者最终都会得到一个未真正初始化的库实例,因为Init()直到稍后才会被调用。

无论如何,这个为构造函数参数导入字符串的技巧就像魅力一样。

答案 1 :(得分:0)

如果您要包含您收到的错误消息,将会有所帮助。

但是,如果你采用左方法,我认为在Window1类上放置PartNotDiscoverableAttribute可能会解决问题。

问题是DirectoryCatalog将包含包含Window1的程序集,因此目录中将提供IError导出(如果您请求导出的值,MEF将创建Window1的实例)。当您添加通过ComposeParts创建的Window1时,您尝试将另一个IError导出添加到容器中。由于您的插件仅请求单个IError导出,因此当有多个可用时,它将无法工作。在Window1类上添加PartNotDiscoverableAttribute将阻止它包含在目录中。