Возникли проблемы с разрешением импорта MEF - PullRequest
0 голосов
/ 20 апреля 2010

Это своего рода продолжение одного из моих предыдущих постов, которое касается разрешения модулей в моем приложении 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
{
    ...
}

Правостороннее приближение

В основном так же, как и в левостороннем подходе, за исключением того, что я создал класс, который наследуется от IError в той же сборке, что и Window1. Затем я использовал [Import], чтобы попытаться заставить MEF решить эту проблему для меня.

Может кто-нибудь объяснить, как два подхода, которые я подошел к MEF, здесь несовершенны? Я был в неведении так долго, что вместо того, чтобы читать о MEF и пробовать разные предложения, я добавил MEF к своему решению и вхожу в код. Часть, где он выглядит так, как будто он терпит неудачу, это когда он вызывает partManager.GetSavedImport(). По какой-то причине importCache имеет значение null, чего я не понимаю. Вплоть до этого момента он просматривал часть (Window1) и пытался разрешить два импортированных интерфейса - IError и IPlugin. Я ожидал, что он введет код, который просматривает другие сборки в той же исполняемой папке, а затем проверит его на экспорт, чтобы он знал, как разрешить импорт ...

Я обнаружил ошибку в своем коде, и когда я исправил ее, исключение MEF изменилось и стало более полезным. В нем четко указано, что не удалось найти конструктор CandySettings по умолчанию! И копая больше, я нашел хороший пост от Гленна Блока , который обсуждает это. Поэтому мне нужно закончить читать и посмотреть, подойдет ли его обходной путь или нет. Я все равно буду признателен за дополнительные ответы, поскольку неясно, является ли обходной путь правильным или нет.

Ответы [ 2 ]

1 голос
/ 20 апреля 2010

Этот пост действительно помог. Я не видел эту информацию раньше, но это полностью помогло мне.

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

По сути, моя проблема заключалась в том, что мне нужно было передать значения конструктору. Все мои прошлые тесты включали передачу интерфейсов в другие разделяемые библиотеки, но в моем случае я просто хотел передать пару строк. Я, очевидно, не хотел пытаться обернуть эти строки в интерфейс только для передачи POCO.

Моя первая попытка обойти это неудобство состояла в том, чтобы сделать все возможное с конструктором по умолчанию. Затем я оставил на произвол судьбы, что разработчик не забудет вызвать метод Init (). Это было плохо по понятным причинам, но я все равно хотел попробовать. В конце концов, это просто не сработало - проблема в том, что MEF хочет разрешить импорт и экспорт, но мой метод Init () не вызывался до после составления частей ... поэтому любые другие иждивенцы этой конкретной библиотеки в конечном итоге получат неинициализированный экземпляр библиотеки, так как Init () не будет вызван позднее.

Так или иначе, этот трюк импорта строк для параметров конструктора работал как шарм.

0 голосов
/ 20 апреля 2010

Было бы полезно, если бы вы включили сообщение об ошибке, которое вы получаете.

Однако, если вы выберете левый подход, я думаю, что добавление PartNotDiscoverableAttribute в ваш класс Window1 может решить проблему.

Проблема в том, что DirectoryCatalog будет включать сборку, которая включает в себя Window1, поэтому в каталоге будет доступен экспорт IError (и MEF создаст экземпляр Window1, если вы запросите значение этого экспорта). Когда вы добавляете Window1, созданный вами через ComposeParts, вы пытаетесь добавить еще один экспорт IError в контейнер. Поскольку ваш плагин запрашивает только один экспорт IError, он не будет работать, если доступно более одного. Добавление атрибута PartNotDiscoverableAttribute в класс Window1 предотвратит его включение в каталог.

...