【问题标题】:How can I handle recursive composition in MEF?如何在 MEF 中处理递归组合?
【发布时间】:2013-09-08 08:37:43
【问题描述】:

考虑以下代码示例,该示例使用 MEF 创建 Importer 类型的对象,该对象导入 ImporterExporter 类型的对象,该对象又导入 Exporter 类型的对象,即 Importer -> ImporterExporter -> Exporter。该目录由CompositionUtility 管理(对于此示例显然已简化)。

我知道 MEF 将递归解析导入部件的导入。但是,因为我希望可以选择独立地实例化这些类中的每一个,所以每个具有导入的类也会在其构造函数中组合自己来解析这些导入。

using System;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;

namespace MefRecursionSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // var importerExporter = new ImporterExporter(); // include this and composition will work
            var importer = new Importer();
            Console.Write(importer.ImporterExporter.Exporter.Value); // should print 7
            Console.ReadKey();
        }
    }

    class CompositionUtility
    {
        static CompositionUtility()
        {
            var executingAssembly = Assembly.GetExecutingAssembly();
            var assemblyCatalog = new AssemblyCatalog(executingAssembly);
            _compositionContainer = new CompositionContainer(assemblyCatalog);
        }

        private static CompositionContainer _compositionContainer;
        private static bool _isComposing;

        public static void Compose(object part)
        {
            _compositionContainer.ComposeParts(part);
        }
    }

    class Importer
    {
        public Importer()
        {
            CompositionUtility.Compose(this);
        }

        [Import]
        public ImporterExporter ImporterExporter { get; set; }
    }

    [Export]
    class ImporterExporter
    {
        public ImporterExporter()
        {
            CompositionUtility.Compose(this);
        }

        [Import]
        public Exporter Exporter { get; set; }
    }

    [Export]
    class Exporter
    {
        public int Value { get { return 7; } }
    }

}

按原样运行代码会导致组合错误“无法重新组合 MefRecursionSample.Importer 类型的 ComposablePart....”,显然是因为我试图明确组合 MEF 也想要组合的东西。

令我惊讶的是,当我包含 Main 方法的第一行时,即在没有 MEF 的情况下创建 ImporterExporter 类型的对象时,这种“双重组合”不再导致异常。这是为什么呢?

另外,我怎样才能使它工作,以便我可以独立地实例化这些中的每一个,同时也让它们在像示例中那样被链接时自行组合。我想我会在CompositionUtility 上引入一个布尔标志_compositionInProgress,并在设置该标志以避免递归组合时立即从Compose() 返回。有没有更好的办法?

【问题讨论】:

    标签: c# recursion mef


    【解决方案1】:

    我考虑在CompositionUtilityCompose 方法中设置的标志不起作用,因为可能会出现自动导入字符串被中断的情况。例如,在问题的示例中,如果 Exporter 在其构造函数中使用 new 实例化了一个类,并且该类想要自己组合。在最初的解决方案下,该类对Ccompose 的调用将立即返回,从而使该类未组合。

    因为我希望类自己组成(因此他们的用户甚至不需要了解 MEF),唯一的解决方案是建立具有[Export] 属性的类不得调用Compose(this) 的规则。因为它们在导入时会被 MEF 自动合成,这会导致“双重合成”,从而引发异常。

    如果要求标有[Export] 的类必须通过new 独立实例化,而不是通过MEF 仅导入,则它们必须有一个带有布尔标志的附加构造函数,当设置好时触发该类的组合.然而,默认行为必须是无合成,以避免上述“双重合成”。

    【讨论】:

      【解决方案2】:

      为什么不简单地这样做呢?

      class Program
      {
          private static CompositionContainer _compositionContainer;
      
          static void Main(string[] args)
          {
              //compose the container just one time in your app
              var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
              _compositionContainer = new CompositionContainer(assemblyCatalog);
      
              var importer = _compositionContainer.GetExportedValue<Importer>();
      
              Console.Write(importer.ImporterExporter.Exporter.Value); // should print 7
              Console.ReadKey();
          }
      }
      
      [Export]
      class Importer
      {
          [ImportingConstructor]
          public Importer(ImporterExporter imex)
          {
              this.ImporterExporter = imex;
          }
      
          public ImporterExporter ImporterExporter { get; private set; }
      }
      
      [Export]
      class ImporterExporter
      {
          [ImportingConstructor]
          public ImporterExporter(Exporter exporter)
          {
              this.Exporter = exporter;
          }
      
          public Exporter Exporter { get; private set; }
      }
      
      [Export]
      class Exporter
      {
          public int Value { get { return 7; } }
      }
      

      【讨论】:

      • 我真的很想从它的构造函数中组合一个对象(除非已经在一个组合中),所以该类的用户不必担心 MEF 组合,并且可以像使用任何其他类一样使用该类.此外,这可以将组合引入现有类。
      【解决方案3】:

      您真正想做的(我认为)是在对象上调用 container.SatisfyImportsOnce() 而不是 ComposeParts 。

      ComposeParts 将所有导出树添加到目录中,而 SatisfyImportsOnce 每个对象都是自己的,组成部分就是这样,没有注册递归导出,因此您可以调用构造函数或使用导入构造函数,您可以同时拥有。

      詹姆斯。

      【讨论】:

        猜你喜欢
        • 2014-05-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-09-26
        • 2012-05-02
        • 2017-03-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多