У меня недавно возникла круговая зависимость из-за изменения архитектуры моего приложения.
Приложение опирается на менеджер плагинов, который загружает плагины через 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
, почему у меня возникла проблема?