我正在研究基于MEF或统一的.net框架上的插件模型。
问题是我还没有找到订购插件执行的解决方案。
假设存在一个由插件组成的执行管道,这些插件之间存在多种关系:某些插件依赖于另一个插件,只有在调用该插件后才能调用它们。应该在管道末端调用一些插件等。
配置文件可能是xml或其他任何东西,它并不重要。令我困惑的是订单算法。
依赖树可以解决,但我不知道它是否足够。有没有成熟的解决方案?关于这个的任何开源项目?或任何建议?
更多解释。
假设我正在编写文本编辑器,这个编辑器支持多个插件,在用户完成工作并保存后,将调用插件执行管道。一些插件在xaml上运行,一些在ubb代码上运行,并且有一个插件传输xaml到ubb。 因此,应首先调用xaml上的所有插件,然后调用插件将xaml调用到ubb,然后调用插件在ubb上工作。 这是插件依赖和排序的示例,这些插件之间可能存在更复杂的关系。 那么,如何以通用的方式解决这个问题呢?
答案 0 :(得分:0)
答案 1 :(得分:0)
我认为您正在寻找的是能够按依赖关系排序。我使用了类似的东西,我创建了一个Bootstrapper对象来管理应用程序启动。此Bootstrapper支持0个或更多引导程序任务,这些任务可能有也可能没有依赖项。我解决这个问题的方法是创建一个新的集合类型,DependencyList<TModel, TKey>
允许您添加任意数量的项目,它将自动排序第一次枚举,或者在任何后续集合更改后。
就您想要做的事情而言,我们既可以利用这种新的列表类型,也可以利用自定义的MEF导出信息。我们要开始的第一个地方是我们的基础管道插件合同:
public interface IPipelinePlugin
{
void Process(PipelineContext context);
}
public abstract class PipelinePluginBase : IPipelinePlugin
{
public virtual void Process(PipelineContext context)
{
}
}
我更喜欢添加一个抽象类,所以如果需要,我可以引入一些基本的共享逻辑,而不会破坏现有的插件。
接下来我们要做的是,定义元数据合约,然后定制自定义导出属性:
public interface IPipelinePluginMetadata
{
string Name { get; }
string[] Dependencies { get; }
string[] Pipelines { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Method, AllowMultiple = false)]
public class PipelineAttribute : ExportAttribute, IPipelinePluginMetadata
{
public PipelineAttribute(string name)
: base(typeof(IPipelinePlugin))
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("A pipeline plugin requires a name.", "name");
Name = name;
}
public string Name { get; private set; }
public string[] Dependencies { get; set; }
public string[] Pipelines { get; set; }
}
使用自定义导出属性,我可以定义导出的形状,以确保它们都输出正确的信息。
接下来,我们来看一个自定义插件。让我们假设我们想要创建一个管道来将BBCode装饰应用于我们的输入文本,所以首先,一个简单的插件:
[Pipeline("ApplyColour", Pipelines = new[] { "bbcode" })]
public class ApplyColourPipelinePlugin : PipelinePluginBase
{
public override void Process(PipelineContext context)
{
context.Content = "[color=f00]" + context.Content + "[/color]";
}
}
上面的示例,只是将输入文本包装在[color]
标记中。 Pipeline
属性详细说明了插件名称(ApplyColour
)以及可供插件访问的管道,在本例中为bbcode
。这是一个更复杂的例子:
[Pipeline("MakeBold", Pipelines = new[] { "bbcode" })]
public class MakeBoldPipelinePlugin : PipelinePluginBase
{
public override void Process(PipelineContext context)
{
context.Content = "[b]" + context.Content + "[/b]";
}
}
[Pipeline("MakeItalic", Dependencies = new[] { "MakeBold" }, Pipelines = new[] { "bbcode" })]
public class MakeItalicAfterBoldPipelinePlugin : PipelinePluginBase
{
public override void Process(PipelineContext context)
{
context.Content = "[i]" + context.Content + "[/i]";
}
}
在上面的例子中,我详细介绍了两个额外的插件,一个使文本变为粗体,并且就是斜体。 但是,我已经介绍了一个依赖项要求,并告诉我们的插件系统,MakeItalic
依赖于MakeBold
。这就是我们把它放在一起的方式:
[Export]
public class PipelineManager
{
[ImportMany]
public IEnumerable<Lazy<IPipelinePlugin, IPipelinePluginMetadata>> Plugins { get; set; }
public Queue<IPipelinePlugin> BuildPipeline(string name)
{
// Get the plugins.
var plugins = Plugins
.Where(p => p.Metadata.Pipelines == null || p.Metadata.Pipelines.Contains(name)).ToList();
// Create our dependency list.
var dependencyList = new DependencyList<Lazy<IPipelinePlugin, IPipelinePluginMetadata>, string>(
l => l.Metadata.Name,
l => l.Metadata.Dependencies);
// Add each available plugin to the list.
plugins.ForEach(dependencyList.Add);
// Create our pipeline.
var pipeline = new Queue<IPipelinePlugin>();
// Now, when we enumerate over it, it will be sorted.
dependencyList.ForEach(p => pipeline.Enqueue(p.Value));
return pipeline;
}
}
我们的PipelineManager
类型由MEF提供支持,因此它会[Import]
一系列IPipelinePlugin
个实例及其关联的元数据(应将其设计为可投影为IPipelinePluginMetadata
})。考虑到这一点,这里正在使用:
class Program
{
static void Main(string[] args)
{
var container = new CompositionContainer(new AssemblyCatalog(typeof(Program).Assembly));
var manager = container.GetExportedValue<PipelineManager>();
var pipeline = manager.BuildPipeline("bbcode");
var context = new PipelineContext("Hello World");
foreach (var plugin in pipeline)
plugin.Process(context);
Console.Write(context.Content);
Console.ReadKey();
}
}
真的是依赖列表,你的管道设计是两个独立的领域,但我希望这可以让你了解如何使用它。
我也把它作为一个要点(https://gist.github.com/1752325)。