Как я могу обработать рекурсивную композицию в MEF? - PullRequest
1 голос
/ 02 февраля 2012

Рассмотрим следующий пример кода, который использует 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; } }
    }

}

Запуск кода как есть приводит к композицииошибка "ComposablePart типа MefRecursionSample.Importer 'не может быть перекомпонована ....", очевидно, потому что я пытаюсь явно составить что-то, что MEF также хочет составить.Я включил первую строку метода Main, т.е. создал объект типа ImporterExporter без MEF, эта «двойная композиция» больше не вызывала исключения.Почему это так?

Кроме того, как я мог заставить его работать так, чтобы я мог создавать каждый из них независимо, но также заставлять их составлять себя, когда они связаны, как в образце.Я подумал, что введу логический флаг _compositionInProgress на CompositionUtility и сразу же вернусь с Compose(), когда флаг установлен, чтобы избежать рекурсивной композиции.Есть ли лучший способ?

Ответы [ 3 ]

2 голосов
/ 18 февраля 2012

Флаг, который я рассматривал, установив в методе CompositionUtility Compose, не работает, потому что могут быть случаи, когда строка автоматического импорта прерывается. Например, в примере вопроса, если Exporter создает экземпляр класса в его конструкторе, используя new, и этот класс захочет составить сам. Согласно исходному решению, вызов этого класса в Ccompose будет возвращаться немедленно, оставляя класс не скомпонованным.

Поскольку я хочу, чтобы классы составляли сами себя (таким образом, их пользователям не нужно даже знать о MEF), единственным решением было установить правило, согласно которому классы с атрибутом [Export] не должны вызывать Compose(this). Поскольку они будут автоматически составлены MEF при импорте, это приведет к «двойной композиции» и, следовательно, к исключению.

Если требуется, чтобы классы, отмеченные [Export], создавались независимо независимо от new, а не импортировались только через MEF, они должны иметь дополнительный конструктор с логическим флагом, который при правильной установке запускает композицию этого класса. , Поведение по умолчанию, однако, должно быть без композиции , чтобы избежать вышеупомянутой "двойной композиции".

0 голосов
/ 08 сентября 2013

То, что вы действительно хотите сделать (я думаю), это вызвать container.SatisfyImportsOnce () для объекта, а не ComposeParts.

ComposeParts добавляет все дерево экспорта в каталог, тогда как SatisfyImportsOnce каждый объект является для себя, составляячасти и все, нет рекурсивного рекурсивного экспорта, так что вы можете вызвать конструктор или использовать импортный конструктор, вы можете иметь и то, и другое.

Джеймс.

0 голосов
/ 03 февраля 2012

почему бы просто не сделать это?

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