Jaa


每周源代码42- Tree Trim、插件和MEF

[原文发表地址] The Weekly Source Code 42 - Tree Trim, Plugins, and MEF

[原文发表时间] 2009-05-20 01:41

我十分提倡大家尽可能多地阅读素材,因为如果要提升自我,使自己成为一名更好的作家,就必须勤读勤练笔。这是《每周源代码》的初衷——勤读代码,有助于成为更高水准的程序开发者。

勤读开放式源码项目中的代码是一种很好的学习方法,尤其是对于存在多时且成功的项目,或者是你所尊重的团队成员所做的项目。就算你对整个项目不甚了解,至少,通过检索和共享代码,你能找到一些代码片段。

Tree Trim== CleanSources++

多年前,Omar Shahine编写了一个非常棒的小程序,叫做Clean Sources。它在Windows浏览器原有基础上添加了右键菜单功能,可以方便你删除bin, obj 和安装文件夹。

之后,Jeff 又编写了Clean Sources Plus,新增了“清除和打包”选项,同时支持移除源代码管理绑定。

如今,Steve Dunn也在此基础上继续扩展,新创了Tree Trim。这是一个命令行工具,拥有上述所有功能并新加了更多功能。他在原有基础上扩展并包含了一个插件模型,使其能在各插件间建立一个小小的通道。这样,你就能将插件链接起来,并用你自己的插件扩展命令行。而MEF(Managed Extensibility Framework: 托管扩展框架 ) 则是其核心。

对于做构建的服务器,使用Tree Trim能够很方便地在你需要拷贝文件,删除源代码管理绑定,打包文件,邮件传送等时候发挥作用。

每个命令行参数是一个“任务”,而每个参数(别称)则映射到不同的目标对象。

treetrim.console.exe c:\dev\myproject -workingCopy -deleteFromDisk -zip –email

参数的顺序问题非常重要。参数保证所有的插件运行有序,比如:先拷贝,再删除bin/obj,打包,然后发送邮件。

制作你自己的插件熟练掌握MEF

他有一个IPlugin插口:

    1: public interface IPlugin
    2:  
    3: {
    4:  
    5: string Moniker { get ; }
    6:  
    7: string WorkingPath { get ; }
    8:  
    9: void Cleanup( ) ;
   10:  
   11: void Run(IPluginRuntimeSettings settings, IPlugin lastPlugin);
   12:  
   13: }

制作插件时,你要让MEF知道类型导出是可行的:

    1: [Export(typeof(IPlugin))]
    2:  
    3: public class SomePlugin : IPlugin
    4:  
    5: {
    6:  
    7: ...
    8:  
    9: public string Moniker
   10:  
   11: {
   12:  
   13: get { return @"newPluginArgument" ; }
   14:  
   15: }
   16:  
   17: ...
   18:  
   19: }

然后同一目录下的所有插件都会被拉到插件清单中

    1: public DiscoverPluginsInAssemblyDirectory( )
    2:  
    3: {
    4:  
    5: var catalog = new DirectoryCatalog(disk.DirectoryOfExecutingAssembly);
    6:  
    7: var container = new CompositionContainer(catalog);
    8:  
    9: var batch = new CompositionBatch();
   10:  
   11: batch.AddPart(this);
   12:  
   13: container.Compose(batch);
   14:  
   15: }
   16:  
   17: [Import( typeof( IPlugin ) )]
   18:  
   19: public IList<IPlugin> Plugins
   20:  
   21: {
   22:  
   23: get;
   24:  
   25: set;
   26:  
   27: }

该应用程序通过导入的命令行参数和找到的插件来启动内部管道:

    1: Trimmer.TrimTree(
    2:  
    3: new TaskCollection( pluginDiscoverer.DiscoveredPlugins, commandLineArgs ),
    4:  
    5: path );

…然后…

    1: public static void TrimTree(ITaskCollection tasks, string sourceTreeRoot)
    2:  
    3: {
    4:  
    5: ITask lastTask = new Task { Plugin = new NullPlugin( sourceTreeRoot ) } ;
    6:  
    7: foreach ( ITask eachTask in tasks )
    8:  
    9: {
   10:  
   11: eachTask.Run( lastTask );
   12:  
   13: lastTask = eachTask ;
   14:  
   15: }
   16:  
   17: IEnumerable<ITask> reversedTasks = tasks.Reverse( ) ;
   18:  
   19: foreach (ITask eachTask in reversedTasks)
   20:  
   21: {
   22:  
   23: eachTask.Cleanup();
   24:  
   25: }
   26:  
   27: }

代码其实简单易懂,而且在Google Code中搜索TreeTrim首行就有显示,你也可以参考FAQ(常见问题)。有了MEF,插件问题就变得相当简单了。同时,Tree Trim在很多方面都给出了不错的示范。首先,它是作为一个广义上的插件,同时它又展现了给插件传递设置的技术。

为程序管理器添加环境菜单

在程序管理器中给这个(或任何其他)工具添加环境菜单很简单。操作如下:在注册表项中添加一个关键字。

HKEY_CLASSES_ROOT\Folder\shell\<WHATEVER TEXT YOU WANT>\command

然后在(默认)字符串中,如下输入(这里为一个示例):

"C:\Program Files (x86)\Tree Trim\TreeTrim.Gui.exe" "%1" -workingcopy -deletefromdisk - zip:writeTo: "c:\users\scott\desktop \justzipped.zip" +dontCleanUp

在注册表项中显示如下:

clip_image002

Steve还在着手使用XUnit进行测试。他试着用ContextSpecification模式进行测试,我们也拭目以待他的测试能否顺利完成。这部分内容到现在为止还算是基础的。可以参考TaskCollectionSpecs.cs,以此为例。

总之,读之有趣,用之便捷。这是个非常有趣的工具,能让我快速清理和发送代码样本。我可能会用我的插件来扩展这个工具,将代码上传至我的博客,并在剪贴板中加入链接。那样撰写代码示例的博客也会变得更简单。