如何处理MEF中的递归合成?

时间:2012-02-02 18:14:01

标签: c# recursion mef

考虑以下代码示例,该示例使用MEF创建类型为Importer的对象,该对象导入类型为ImporterExporter的对象,该对象又导入类型为Exporter的对象,即{{ 1}}。目录由Importer -> ImporterExporter -> Exporter管理(本例中显然简化了)。

我知道MEF将在导入的部件上递归解析导入。但是,因为我希望能够独立地实例化每个类,所以每个带有导入的类也会在其构造函数中自行组合以解决这些导入。

CompositionUtility

运行代码会导致合成错误“MefRecursionSample.Importer类型的ComposablePart'无法重构......”,显然是因为我试图明确地撰写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; } } } } 方法的第一行,即创建一个没有MEF的Main类型的对象时,这种“双重构图”不再导致例外。那是为什么?

另外,我怎么能让它工作,以便我可以独立地实例化每一个,但是当它们像样本一样被链接时也会使它们自己组合。我想我会在ImporterExporter上引入一个布尔标志_compositionInProgress,并在设置标志时立即从CompositionUtility返回以避免递归合成。还有更好的方法吗?

3 个答案:

答案 0 :(得分:2)

我考虑在CompositionUtility的{​​{1}}方法中设置的标志不起作用,因为可能存在自动导入字符串被中断的情况。例如,在问题的示例中,如果Compose使用Exporter在其构造函数中实例化了一个类,则此类将要自行编写。在原始解决方案下,该类对new的调用将立即返回,使该类不合成。

因为我希望类自己编写(从而使其用户甚至不需要知道MEF),唯一的解决方案是建立具有Ccompose属性的类不能调用{{1}的规则}。因为它们将在导入时由MEF自动组合,这将导致“双重组合”,从而引发异常。

如果要求标记为[Export]的类必须通过Compose(this)独立实例化而不是通过MEF导入,则必须有一个带布尔标志的附加构造函数,当设置为良好触发时那个班级的组成。但是,默认行为必须是 no composition ,以避免上述“双重构成”。

答案 1 :(得分:0)

为什么不简单地这样做?

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; } }
}

答案 2 :(得分:0)

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

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

詹姆斯。