Этот вопрос может быть связан с дизайном или кодом, но я застрял, поэтому я открыт для любого ответа; указатель в правильном направлении!Попытка и забыть подход к архитектуре плагина MEF
Я использовал MEF (Managed Extensibility Framework) для разработки части программного обеспечения WPF, которая будет выступать в качестве формы оркестра для плагинов. Приложение просто перенаправляет данные между плагинами по выбору пользователя, поэтому то, что делает плагин, вообще не известно (особенно, поскольку они могут быть разработаны сторонними разработчиками). Приложение и плагин совместно используют интерфейс как способ узнать, какие методы нужно вызвать, поэтому трафик идет в обоих направлениях: плагин вызывает метод в основном приложении, отправляя его данные, и основное приложение передает эти данные другому плагин.
Это работает до сих пор, но у меня проблема синхронного поведения. Все методы, определенные интерфейсом, не имеют возвращаемого значения (Void), и я изо всех сил пытаюсь получить подход «огонь и забыть», когда вызывающему приложению НЕ нужно сидеть, ожидая, что функция получения плагинов завершит выполнение кода (и звонки, которые возвращаются в основное приложение!).
Итак, какой лучший подход к решению этого? Предоставление каждому плагину (и основному приложению) его рабочей нагрузки на «стек» какого-то типа, чтобы иметь возможность возвращать элемент управления вызывающей стороне, а затем иметь некоторый механизм, который выполняется отдельно, который работает через элемент стека по элементу (и сделать этот метод стекирования как async?)?
Следует обратить внимание на то, что плагины работают в отдельных потоках (в зависимости от окна потока отладчика), и когда они инициализируются, они получают ссылку из главного приложения-вызова, чтобы они могли запускать функции в главном приложении. Плагинам также очень часто приходится указывать основному приложению, в каком статусе они находятся (бездействуют, работают, об ошибке и т. Д.), А также отправляют данные для регистрации в главном приложении, поэтому это очень часто создает иерархию вложенных вызовов (если вы следуете за мной , сложно объяснить).
Я использую .Net 4.5 для этого.
Ниже приведено несколько упрощенных примеров кода. Я заменил некоторые имена, поэтому, если где-то есть орфографическая ошибка, это просто здесь, а не в реальном коде. :)
Интерфейс:
public interface IMyPluggableApp
{
void PluginStatus(string PluginInstanceGuid, PluginInstanceState PluginInstanceState);
void DataReceiver(string PluginInstanceGuid, string ConnectorGuid, object Data);
void Logg(string PluginInstanceGuid, LoggMessageType MessageType, string Message);
}
public interface IPluginExport
{
PluginInfo PluginInfo { get; set; }
void Initialize(string PluginInstanceGuid, Dictionary<string, string> PluginUserSettings, IMyPluggableApp MyPluggableApp);
void Start(string PluginInstanceGuid, List<ConnectorInstanceInfo> ConnectedOutputs);
void Stop(string PluginInstanceGuid);
void PluginClick(string PluginInstanceGuid);
void PlugginTrigger(string ConnectorGuid, object Data);
}
Плагин:
public static IMyPluggableApp _MyPluggableApp
[PartCreationPolicy(CreationPolicy.NonShared)]
[Export(typeof(IPluginExport))]
public class PluginExport : IPluginExport
{
public void Initialize(string PluginInstanceGuid, Dictionary<string, string> pluginUserSettings, IMyPluggableApp refMyPluggableApp)
{
_MyPluggableApp = refMyPluggableApp; // Populate global object with a ref to the calling application
// some code for setting saved user preferences
_MyPluggableApp.PluginStatus(PluginInfo.PluginInstanceGuid, PluginInstanceState.Initialized); // Tell main app we're initialized
}
public void Start(string PluginInstanceGuid, List<ConnectorInstanceInfo> ConnectedOutputs)
{
// Some code for preparing the plugin functionality
_MyPluggableApp.PluginStatus(PluginInfo.PluginInstanceGuid, PluginInstanceState.Initialized); // Tell main app we started
}
public void PlugginTrigger(string ConnectorGuid, object Data)
{
_MyPluggableApp.PluginStatus(AvailablePlugins.PluginInfo.PluginInstanceGuid, PluginInstanceState.Running_Busy); // Tell main app we're busy
// Run the code that actually provides the functionality of this plugin
_MyPluggableApp.PluginStatus(AvailablePlugins.PluginInfo.PluginInstanceGuid, PluginInstanceState.Running_Idle); // Tell main app we're idle
}
// and so on ...
}
И главное приложение:
public partial class MainWindow : IMyPluggableApp
{
[ImportMany(typeof(IPluginExport))]
IPluginExport[] _availablePlugins;
public void PluginStatus(string PluginInstanceGuid, PluginInstanceState PluginInstanceState)
{
// Code for setting status in GUI
}
public void DataReceiver(string PluginInstanceGuid, string ConnectorGuid, object Data)
{
ConnectorInfo connector_source = GetConnectorInfo(ConnectorGuid);
PluginInfo plugin_source = GetPluginInfo_ByPluginInstanceGuid(PluginInstanceGuid);
ConnectorInstanceInfo connector_destination = (from i in _project.PluginInstances
from y in i.ConnectedConnectors
where i.PluginInstanceGuid == PluginInstanceGuid
&& y.ConnectedFromOutput_ConnectorGuid == ConnectorGuid
select y).FirstOrDefault();
_availablePlugins.Where(xx => xx.PluginInfo.PluginInstanceGuid == connector_destination.ConnectedToInput_PluginInstanceGuid).First().PlugginTrigger(ConnectorGuid, Data);
}
public void Logg(string PluginInstanceGuid, LoggMessageType MessageType, string Message)
{
// Logg stuff
}
}
Это функция DataReceiver в основном приложение Thats получает данные , смотрит, какой плагин должен иметь его, а затем отправляет его (через функцию PlugginTrigger).
Можете ли вы опубликовать упрощенную версию вашего интерфейса плагина и пример того, как приложение в настоящее время вызывает метод на одном из плагинов? – Matt
(добавленный пример кода) – RobertN