У меня есть круговая зависимость, недавно появившаяся из-за изменения архитектуры приложения.Ошибка MEF, была круговой зависимостью и теперь что-то еще
Приложение использует диспетчер плагинов, который загружает плагины через 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. Затем я использовал модель.
[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
}
}
второй реализация довольно четко циклическая ссылка, потому что плагины требует создания PluginQueryInterface до создания плагина, но t он PluginQueryInterface - это модель, которая должна импортировать PluginManager, который, в свою очередь, нуждается во всех созданных PluginInterfaces ... и при запуске я получаю ошибку зависимости MEF.
Реализация не кажется круговой ссылкой на меня. Если PluginQueryInterface является свойством, то я думал, что он не будет разрешен , пока не будет использован. И он вообще не используется конструктором. Так почему бы не PluginManager весело создавать все мои MyPlugins? Я получаю ту же ошибку MEF в обоих случаях.
Я попытался решить эту проблему, создав PluginManager для реализации PluginQueryInterface, потому что a) имеет смысл в любом случае и b) это known way of dealing with circular dependencies - заставьте два взаимозависимых класса вместо этого зависеть от третьего класса. Теперь проблема в том, что я получаю другую ошибку 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
?
UPDATE
Я не понимаю эту часть 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 не собирались делать. I мог слепо прокомментировать this._importValues.Remove(definition);
, но это, возможно, не так. Я предполагаю, что это сводится к атрибутам MEF, которые я использовал, но так как плагин, который импортирует это значение, имеет политику создания CreationPolicy.Shared
, почему у меня есть проблема?
Я буду смотреть на флаг THREADSAFE. Я даже не осознал, что есть один! – Dave