这个问题可能与设计或代码有关,但我陷入困境,所以我接受任何类型的答案;指向正确方向的指针!
我使用MEF(托管可扩展性框架)开发了一款作为插件协调器的WPF软件。该应用程序仅根据用户选择在插件之间重定向数据,因此根本不知道插件的功能(特别是因为它们可以由第三方开发者开发)。应用程序和插件共享一个接口来了解要调用哪些方法,因此流量是双向的:插件调用主应用程序中的方法发送数据,然后主应用程序将此数据传递给另一个插件。
到目前为止,这是有效的,但我在同步行为方面遇到了问题。界面定义的所有方法都缺少返回值(Void),我正在努力实现“点火并忘记”这种方法,即调用应用程序无需等待插件接收函数完成执行代码(以及回到主应用程序的调用!)。
那么解决这个问题的最佳方法是什么?让每个插件(和主应用程序)将其工作负荷放在某种“堆栈”上,以便能够将控制权返回到调用方,然后有一些机制单独运行,按项处理堆栈项(并且将此堆栈方法作为异步执行)?
值得注意的其他事情是插件在单独的线程中运行(根据调试器线程窗口),并且当它们被初始化时,它们会从调用主应用程序获取引用,以便可以在主应用程序中触发函数。插件还经常需要告诉主应用程序它们处于什么状态(空闲、工作、错误等),并发送要由主应用程序记录的数据,因此这往往会创建一个嵌套的调用层次结构(如果你跟我走,很难解释)。
我在这个问题上使用了.Net 4.5。
以下是某些代码的简化示例。我替换了一些名称,因此如果某处存在拼写错误,则只存在于此处而不是真实代码中。 :)
接口:
插件:
我使用MEF(托管可扩展性框架)开发了一款作为插件协调器的WPF软件。该应用程序仅根据用户选择在插件之间重定向数据,因此根本不知道插件的功能(特别是因为它们可以由第三方开发者开发)。应用程序和插件共享一个接口来了解要调用哪些方法,因此流量是双向的:插件调用主应用程序中的方法发送数据,然后主应用程序将此数据传递给另一个插件。
到目前为止,这是有效的,但我在同步行为方面遇到了问题。界面定义的所有方法都缺少返回值(Void),我正在努力实现“点火并忘记”这种方法,即调用应用程序无需等待插件接收函数完成执行代码(以及回到主应用程序的调用!)。
那么解决这个问题的最佳方法是什么?让每个插件(和主应用程序)将其工作负荷放在某种“堆栈”上,以便能够将控制权返回到调用方,然后有一些机制单独运行,按项处理堆栈项(并且将此堆栈方法作为异步执行)?
值得注意的其他事情是插件在单独的线程中运行(根据调试器线程窗口),并且当它们被初始化时,它们会从调用主应用程序获取引用,以便可以在主应用程序中触发函数。插件还经常需要告诉主应用程序它们处于什么状态(空闲、工作、错误等),并发送要由主应用程序记录的数据,因此这往往会创建一个嵌套的调用层次结构(如果你跟我走,很难解释)。
我在这个问题上使用了.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功能,它接收数据,查看哪个插件应该拥有它,然后通过PlugginTrigger函数发送它。