Ошибка MEF, была круговая зависимость и теперь это что-то другое - PullRequest
4 голосов
/ 13 сентября 2010

У меня недавно возникла круговая зависимость из-за изменения архитектуры моего приложения.

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

// model.cs
[Export("Model")]
public class Model
{
  public PluginManager PM { get; set; }

  [ImportingConstructor]
  public Model( [Import] PluginManager plugin_manager)
  {
    PM = plugin_manager;
  }
}

// pluginmanager.cs
[Export(typeof(PluginManager))]
public class PluginManager
{
  [ImportMany(typeof(PluginInterface))]
  private IEnumerable<PluginInterface> Plugins { get; set; }
}

и плагины выглядели так:

// myplugin.cs
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
}

Но теперь у меня есть ситуация, когда я хочу всеплагины, чтобы иметь возможность запрашивать PluginManager (или, возможно, любой другой объект) через интерфейс, чтобы узнать о других плагинах в системе, чтобы узнать об их возможностях.Я «решил» это, добавив другой интерфейс, назовем его PluginQueryInterface.Затем я должен был Model реализовать этот интерфейс.

[Export("Model"))]
[Export(typeof(PluginQueryInterface))]
public class Model : PluginQueryInterface
{
  // same as before
}

и тогда подпись плагина выглядела бы так:

// 1st possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
  [Import(typeof(PluginQueryInterface))]
  public PluginQueryInterface QueryInterface { get; set; }

  public MyPlugin() {}
}

или это

// 2nd possible implementation
[Export(typeof(PluginInterface))]
public class MyPlugin : PluginInterface
{
  private  PluginQueryInterface QueryInterface { get; set; }

  [ImportingConstructor]
  public MyPlugin( [Import] PluginQueryInterface query_interface)
  {
    QueryInterface = query_interface
  }
}

Реализация 2nd довольно четко является круговой ссылкой, поскольку плагины требуют, чтобы PluginQueryInterface был создан до создания плагина, но PluginQueryInterface - это модель, которая должнаимпортируйте PluginManager, которому, в свою очередь, нужны все созданные PluginInterfaces ... и я получаю ошибку циклической зависимости MEF при запуске.

Реализация 1st не выглядит каккруговая ссылка на меня.Если PluginQueryInterface является свойством, то я думал, что оно не будет разрешено , пока не будет использовано .И это вообще не используется конструктором.Так почему бы PluginManager весело не создать все мои MyPlugins?В обоих случаях я получаю одну и ту же ошибку MEF.

Я попытался решить эту проблему, затем заставив PluginManager реализовать PluginQueryInterface, потому что а) это все равно имеет смысл и б) это известный способработа с круговыми зависимостями - вместо этого сделать два взаимозависимых класса зависимыми от третьего класса.Теперь проблема в том, что я получаю другую ошибку MEF !Вот что он говорит:

GetExportedValue cannot be called before prerequisite import 'Company.App.PluginManager..ctor(Parameter="database_filepath", ContractName="PluginManager.filename")' has been set.

WTF?Я установил точки останова в своем коде, и мое экспортированное значение PluginManager.filename было установлено перед вызовом GetExportedValue.

Я полностью озадачен.Любые замечания или предложения будут с благодарностью прямо сейчас.Я часами бился головой об стену в MEF, пытаясь отладить эту проблему.

(обновлено)

Я раньше не думал об этом, но это могло бытьразличия между плагинами, поэтому я удалил один из двух плагинов, и теперь мое приложение загружается без ошибок MEF.Я добавил его обратно, и снова не получилось.Затем я удалил другой плагин, и он работал.Похоже, это какая-то другая ошибка MEF.Как будто бы он не хочет, чтобы я загружал более одного плагина с определенным интерфейсом ... но я использую ImportMany, и разве это не проявилось бы как CardinalityException какого-то рода?

ОБНОВЛЕНИЕ

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

    private bool TryGetImportValue(ImportDefinition definition, out object value)
    {
        lock (this._lock)
        {
            if (this._importValues.TryGetValue(definition, out value))
            {
                this._importValues.Remove(definition); // this is the line that got me
                return true;
            }
        }

        value = null;
        return false;
    }

У меня никогда не было этой проблемы раньше, и, честно говоря, у меняТрудно понять, что я делаю сейчас с моими импортом и экспортом, который сделал эту проблему на поверхность.Я предполагаю, что я делаю то, что дизайнеры MEF не собирались никому делать.Я мог бы вслепую закомментировать this._importValues.Remove(definition);, но это не могло быть правильно.Я предполагаю, что это будет сводиться к атрибутам MEF, которые я использовал, но поскольку плагин, который импортирует это значение, имеет политику создания CreationPolicy.Shared, почему у меня возникла проблема?

Ответы [ 2 ]

3 голосов
/ 16 сентября 2011

Кроме того, это может быть проблема с многопоточностью.Вы должны попытаться создать контейнер как ThreadSafe:

http://blogs.microsoft.co.il/blogs/zuker/archive/2011/01/02/mef-thread-safety-and-getexportedvalue.aspx

3 голосов
/ 13 сентября 2010

Ну, у меня есть возможное решение. У меня нет никакого опыта использования этого, но использование Lazy, кажется, помогает. По крайней мере, я могу двигаться вперед, не меняя код MEF, который я до конца не понимаю.

...