我想你正在尋找的是能夠依賴關係進行排序。我使用了類似的東西,我創建了一個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]
與它們相關的元數據(這應該造型爲可投射爲IPipelinePluginMetadata
)沿着一系列IPipelinePlugin
實例。考慮到這一點,在這裏它是在使用中:
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();
}
}
真的依賴列表中,你的管道設計是兩個不同的領域來看待,但我希望這給你如何可能使用的想法它。
我把它當成了一個Gist(https://gist.github.com/1752325)。
我仍然不能看到mef的任何問題,因爲依賴關係是當你需要它們時組成的。你是否嘗試過遇到錯誤的地方? – blindmeis 2012-02-06 11:43:21
問題不在於mef,這個作品運作良好。問題在於我已經獲得了所有必要的插件實例,如何調用它們?上面的關係要求這些插件按順序調用,我想要解決的是以通用的方式排列這些插件,不僅適用於文本編輯器,還適用於webrequest管道或其他任何東西。就像Matthew的回答一樣,他使用導出的數據庫中的插件名稱並根據它創建依賴關係。這可以解決,但我仍然在尋找更通用的解決方案。 – 2012-02-07 02:44:56