解决 MEF 导入时遇到的问题

发布于 2024-08-29 07:29:53 字数 2641 浏览 10 评论 0原文

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

我尝试了两种方法:

  • 左方法:应用程序实现 IError
  • 右方法:应用程序有一个实现 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 的类。然后我使用 [Import] 尝试让 MEF 帮我解决这个问题。

谁能解释一下我在这里接触 MEF 的两种方法有什么缺陷吗?我已经蒙在鼓里太久了,所以我没有阅读 MEF 并尝试不同的建议,而是将 MEF 添加到我的解决方案中并开始编写代码。看起来失败的部分是调用 partManager.GetSavedImport() 时。由于某种原因, importCache 为空,我不明白。到目前为止,它一直在查看部件 (Window1) 并尝试解析两个导入的接口 - IError 和 IPlugin。我本希望它输入查看同一可执行文件夹中其他程序集的代码,然后检查它的导出,以便它知道如何解析导入...

我在代码中发现了一个错误,当我修复时MEF 例外发生了变化,并且也更加有用。它清楚地指出它找不到 CandySettings 默认构造函数!进一步挖掘,我发现 好Glenn Block 的帖子对此进行了讨论。所以我需要读完它,看看他的解决方法是否有效。我仍然希望获得更多答案,因为无法判断解决方法是否正确。

This is sort of a continuation of one of my earlier posts, which involves the resolving of modules in my WPF application. This question is specifically related to the effect of interdependencies of modules and the method of constructing those modules (i.e. via MEF or through new) on MEF's ability to resolve relationships.

I have tried two approaches:

  • left approach: the App implements IError
  • right approach: the App has a member that implements IError

Left approach

My code behind looked like this (just the MEF-related stuff):

// 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
{
    ...
}

Right-side approach

Basically the same as the left-side approach, except that I created a class that inherits from IError in the same assembly as Window1. I then used an [Import] to try to get MEF to resolve that for me.

Can anyone explain how the two ways I have approached MEF here are flawed? I have been in the dark for so long that instead of reading about MEF and trying different suggestions, I've added MEF to my solution and am stepping into the code. The part where it looks like it fails is when it calls partManager.GetSavedImport(). For some reason, the importCache is null, which I don't understand. All the way up to this point, it's been looking at the part (Window1) and trying to resolve two imported interfaces -- IError and IPlugin. I would have expected it to enter code that looks at other assemblies in the same executable folder, and then check it for exports so that it knows how to resolve the imports...

I had found a mistake in my code, and when I fixed it, the MEF exception changed, and was also more useful. It clearly pointed out that it couldn't find a CandySettings default constructor! And digging more, I found a good post from Glenn Block that discusses this. So I need to finish reading it and see if his workaround will do the trick or not. I would still appreciate more answers, since there's no telling if the workaround is the right thing to do or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

ヅ她的身影、若隐若现 2024-09-05 07:29:53

这篇文章确实很有帮助。我以前没有见过这些信息,但它完全对我有用。

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

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

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

无论如何,这种为构造函数参数导入字符串的技巧非常有效。

This post really helped. I hadn't seen this information before, but it totally did the trick for me.

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

Basically, my problem was that I needed to pass values to the constructor. All of my past tests involved passing interfaces to other shared libraries, but in my case, I just wanted to pass a couple of strings. I obviously didn't want to try to wrap these strings in an interface just to pass POCOs.

My first attempt in getting around this inconvenience was to do the best I could with the default constructor. I then left it up to fate that a developer would remember to call the Init() method. This was bad for obvious reasons, but I wanted to try it out anyway. In the end, it just didn't work -- the problem here is that MEF wants to resolve imports and exports, but my Init() method wouldn't get called until after composing the parts... so any other dependents of that particular library would end up with a not-truly-initialized instance of the library since Init() won't get called until later.

Anyhow, this trick of importing strings for the constructor parameters worked like a charm.

绮筵 2024-09-05 07:29:53

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

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

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

It would help if you would include the error message that you are getting.

However, if you go with the left approach, I think putting a PartNotDiscoverableAttribute on your Window1 class may fix the problem.

The issue is that the DirectoryCatalog is going to include the assembly that includes Window1, so there is going to be an IError export available from the catalog (and MEF would create an instance of Window1 if you requested that export's value). When you add the Window1 you created via ComposeParts, you are trying to add another IError export to the container. Since your plugin is only requesting a single IError export, it won't work when there is more than one available. Adding the PartNotDiscoverableAttribute on the Window1 class will prevent it from being included in the catalog.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文