- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这个问题可能与设计相关或与代码相关,但我卡住了,所以我愿意接受任何类型的答案;正确方法的指针!
我已经使用 MEF(托管可扩展性框架)开发了一款 WPF 软件,该软件将充当插件的一种编排器形式。该应用程序只是根据用户的选择在插件之间重定向数据,因此根本不知道插件的作用(特别是因为它们可以由 3rd 方开发人员开发)。应用程序和插件共享一个接口(interface),以此了解调用哪些方法,因此流量是双向的:插件调用主应用程序中的方法向其发送数据,主应用程序将此数据传递给另一个插件.
到目前为止,这是有效的,但我在同步行为方面遇到了问题。接口(interface)定义的所有方法都缺少返回值(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 函数)。
最佳答案
一些观察:
IsOneWay
property在你的 OperationContractAttribute
上。第二点提出了一种解决方案,这对您的情况来说似乎有点矫枉过正 - 但无论如何我们还是要提一下。您的插件可以托管进程内 WCF 服务,并且 WPF 应用程序和插件之间的所有通信都可以通过 WCF 服务代理完成。然而,这伴随着配置噩梦,并且确实为您必须解决的一大堆其他问题打开了一堆蠕虫。
让我们从初始问题的一个简单示例开始,并尝试从那里解决它。这是带有插件的控制台应用程序的代码:
public class Program
{
private static void Main(string[] args)
{
var host = new CompositionHost();
new CompositionContainer(new AssemblyCatalog(typeof(Plugin).Assembly)).ComposeParts(host);
var plugin = host.Plugin;
plugin.Method();
Console.ReadLine();
}
private class CompositionHost: IPartImportsSatisfiedNotification
{
[Import(typeof (IPlugin))] private IPlugin _plugin;
public IPlugin Plugin { get; private set; }
public void OnImportsSatisfied()
{
Plugin = _plugin;
}
}
}
public interface IPlugin
{
void Method();
}
[Export(typeof(IPlugin))]
public class Plugin : IPlugin
{
public void Method()
{
//Method Blocks
Thread.Sleep(5000);
}
}
问题是对 plugin.Method()
的调用被阻塞了。为了解决这个问题,我们将暴露给控制台应用程序的接口(interface)更改为以下内容:
public interface IAsyncPlugin
{
Task Method();
}
调用此接口(interface)的实现不会阻塞。我们唯一需要更改的是 CompositionHost
类:
private class CompositionHost: IPartImportsSatisfiedNotification
{
[Import(typeof (IPlugin))] private IPlugin _plugin;
public IAsyncPlugin Plugin { get; private set; }
public void OnImportsSatisfied()
{
Plugin = new AsyncPlugin(_plugin);
}
private sealed class AsyncPlugin : IAsyncPlugin
{
private readonly IPlugin _plugin;
public AsyncPlugin(IPlugin plugin)
{
_plugin = plugin;
}
public Task Method()
{
return Task.Factory.StartNew(() => _plugin.Method());
}
}
}
}
显然,这是一个非常简单的示例,在将其应用于您的 WPF 方案时,实现可能会略有不同 - 但一般概念应该仍然有效。
关于c# - MEF 插件架构的即发即弃方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19833323/
我想了解 Ruby 方法 methods() 是如何工作的。 我尝试使用“ruby 方法”在 Google 上搜索,但这不是我需要的。 我也看过 ruby-doc.org,但我没有找到这种方法。
Test 方法 对指定的字符串执行一个正则表达式搜索,并返回一个 Boolean 值指示是否找到匹配的模式。 object.Test(string) 参数 object 必选项。总是一个
Replace 方法 替换在正则表达式查找中找到的文本。 object.Replace(string1, string2) 参数 object 必选项。总是一个 RegExp 对象的名称。
Raise 方法 生成运行时错误 object.Raise(number, source, description, helpfile, helpcontext) 参数 object 应为
Execute 方法 对指定的字符串执行正则表达式搜索。 object.Execute(string) 参数 object 必选项。总是一个 RegExp 对象的名称。 string
Clear 方法 清除 Err 对象的所有属性设置。 object.Clear object 应为 Err 对象的名称。 说明 在错误处理后,使用 Clear 显式地清除 Err 对象。此
CopyFile 方法 将一个或多个文件从某位置复制到另一位置。 object.CopyFile source, destination[, overwrite] 参数 object 必选
Copy 方法 将指定的文件或文件夹从某位置复制到另一位置。 object.Copy destination[, overwrite] 参数 object 必选项。应为 File 或 F
Close 方法 关闭打开的 TextStream 文件。 object.Close object 应为 TextStream 对象的名称。 说明 下面例子举例说明如何使用 Close 方
BuildPath 方法 向现有路径后添加名称。 object.BuildPath(path, name) 参数 object 必选项。应为 FileSystemObject 对象的名称
GetFolder 方法 返回与指定的路径中某文件夹相应的 Folder 对象。 object.GetFolder(folderspec) 参数 object 必选项。应为 FileSy
GetFileName 方法 返回指定路径(不是指定驱动器路径部分)的最后一个文件或文件夹。 object.GetFileName(pathspec) 参数 object 必选项。应为
GetFile 方法 返回与指定路径中某文件相应的 File 对象。 object.GetFile(filespec) 参数 object 必选项。应为 FileSystemObject
GetExtensionName 方法 返回字符串,该字符串包含路径最后一个组成部分的扩展名。 object.GetExtensionName(path) 参数 object 必选项。应
GetDriveName 方法 返回包含指定路径中驱动器名的字符串。 object.GetDriveName(path) 参数 object 必选项。应为 FileSystemObjec
GetDrive 方法 返回与指定的路径中驱动器相对应的 Drive 对象。 object.GetDrive drivespec 参数 object 必选项。应为 FileSystemO
GetBaseName 方法 返回字符串,其中包含文件的基本名 (不带扩展名), 或者提供的路径说明中的文件夹。 object.GetBaseName(path) 参数 object 必
GetAbsolutePathName 方法 从提供的指定路径中返回完整且含义明确的路径。 object.GetAbsolutePathName(pathspec) 参数 object
FolderExists 方法 如果指定的文件夹存在,则返回 True;否则返回 False。 object.FolderExists(folderspec) 参数 object 必选项
FileExists 方法 如果指定的文件存在返回 True;否则返回 False。 object.FileExists(filespec) 参数 object 必选项。应为 FileS
我是一名优秀的程序员,十分优秀!