Spiga

Oxite2插件机制分析

2009-09-22 02:36 by xuefly, 2317 visits, 网摘, 收藏, 编辑

由于Oxite1ASP.NET MVC1还未发布的时候就早早地发布了,2009215日发布的Oxite1口碑不好,但是即将发布的经过重新开发的Oxite2已经不再羸弱了,有非常多的值得观察与学习的地方。本文讨论的内容是基于Oxite2的,你可以在这里下载到最新的Oxite源码:http://oxite.codeplex.com/SourceControl/ListDownloadableCommits.aspx

欢迎加入Oxite小组一起学习:博客园Oxite小组

Oxite2的插件机制分析

一般,插件机制是基于接口实现的。但是Oxite2的插件机制是基于“约定”的。既然接口的本意是必须履行的“契约”,即,签订者必须履行的约定(签订者指非抽象类,对契约补充条款还是契约,不是契约的签订者),那么,这样说的话Oxite2的插件机制也可以算是基于“接口”的啦。“契约”是必须履行的,“约定”是非强制履行的。

小组希望我写一些关于Oxite2的插件机制的文字。我以前没有接触过插件机制,因此答应介绍这个话题,因为我可以就着Oxite2的源码趁机学习一下哈。我们小组是Oxite2开源项目学习小组,这篇文章的内容主要是通过对Oxite2代码的学习得来的,所以文章谈论的内容局限于Oxite2的插件机制,并不是ASP.NET插件机制的总结性文章。我尽最大努力保证文章内容的正确性,对于不正确的地方一定要指出来。

您需要深入理解以下内容作为阅读本文的前提:

1, 程序集

2, 反射

3, 特性(Attribute

4, 最好了解:linq, 委托,lambda

另外您还需要一份Oxite2源码

使用System.Web.Compilation.BuildManager动态编译代码文件为程序集

     首先,Oxite2的插件机制是使用BuildManager.GetCompiledAssembly(string virtualPath)方法“将文件编译为‘给定了虚拟路径的程序集’”的。双引号内的文字摘自MSDN,单引号内的内容实在难以理解(查英文MSDN原文为:Compiles a file into an assembly given its virtual path.)希望有理解单引号内的内容的朋友回复。我先不管这个,我们先通过实验熟悉一下该方法,在HomeController中书写如下代码:

public void Compile()

{

    Assembly assembly = BuildManager.GetCompiledAssembly(Request.ApplicationPath + "Plugins/HelloWorld.cs");

 

    Response.Write("assembly.FullName:<br />" + assembly.FullName);

    Response.Write("<br />");

    Response.Write("assembly.Location:<br />" + assembly.Location);

}

è 请求http://localhost:2801/Home/Compile

è 输出结果:

assembly.FullName:
App_Web_helloworld.cs.3b296ce9.iicat0p2,Version=0.0.0.0,Culture=neutral, PublicKeyToken=null
assembly.Location:
C:"Windows"Microsoft.NET"Framework"v2.0.50727"Temporary ASP.NET Files"root"836b6d34"9df6dbcc"App_Web_helloworld.cs.3b296ce9.iicat0p2.dll

好了,对BuildManager的介绍就到这里,更详细的请查看MSDN。下面贴一张图:

说明:作为插件,TwitterPlugins.cs文件的编译选项被设置为Content而不是Compile,对它的编译由我们通过使用BuildManager编码进行而不是使用VS管理编译。

上图Plugins文件夹下的cs代码是和“.aspx”等文件中声明式的代码一样通过BuildManager编译的。区别是“.aspx”等文件是在第一次请求到他们时即时编译并缓存到"Temporary ASP.NET Files"系统目录的,而对插件代码文件的编译是由我们控制的,仅此区别。Oxite2的插件机制对Plugins文件夹递归并把每一个“.cs”或者“.vb”后缀的文件编译成相应的程序集,每一个代码文件对应一个程序集(每一个”.aspx”文件也是对应一个程序集,并且App_Code特殊对待,App_Code中的每一个文件夹编译成一个程序集,对于非App_Code文件夹中的代码WebSite项目和WebApp项目编译方式不同)。

 

在我们了解了如何通过使用BuildManager把代码文件编译为程序集之后,后面的内容就可以直接展开了。

Oxite2插件机制

首先是PluginAssemblyContainerPluginContainer。这两个具有继承关系的类分别是对“插件程序集”和该程序集中的“插件”封装的容器类。


其中图上所说的“类级别特性”全都继承自DefinitionAttribute而“属性级别特性”全都继承自PropertyDefinitionAttribute

由于这两种类型的特性定义的实在太多了,并且Oxite2还有还有第二种方案——使用特定方式命名的属性和匿名类型的属性值取代特性。所以这里就不贴类图上来了。介绍一个我在学习中总结的方法,大家一看便知:比如现在我们要研究Oxite项目的ViewModels文件夹下的类:


我们首先建一个.cd文件,按照一定规则命名该类图文件,然后将ViewModels文件夹拖到该打开的类图文件上,VS就会自动给我们绘制好类图了,如下:


说明:VS绘出的ViewModels文件夹下的类的类结构图

另外VS绘制类图的功能不是仅仅支持源代码,对于dll文件照样是可以的。不过对于dll文件我们需要做的是拖放命名空间到打开的类图上,而不是拖放文件夹或者dll文件。现在你可以进去VS的对象浏览器对任何一个.NET Framework中的命名空间按照上面的说明操作一下试试了。如果有条件的话,把自己不是很熟悉的.NET命名空间下的类打印出来贴在墙上是一个不错的主意。

按照上面介绍的方法对Oxite项目中Plugins文件夹下的代码进行研究很多东西都是一目了然的啦,由于类文件太多,类图太大,大部分类仅仅是离散的用来封装数据的类,贴类图没有价值。所以这里只贴上来类文件列表并对列表上的类做一点说明:


Plugins文件夹下的代码就是Oxite2的插件机制的基础构架,而Oxite项目的Modules文件夹下的Plugins文件夹中的代码正是基于该基础架构的Plugins系统模块的实现。


我们看上图中的Plugins系统模块,从类文件的组织结构看和《浅谈Oxite2的数据访问和数据验证》中所分析的结构完全一样。可以参考上一篇熟悉Plugins的数据访问和业务逻辑层。到这里应该差不多了,其实从通过使用BuildManager编译出程序集开始,下面就是很明显的方案了:加载程序集并维护一个插件程序集列表 => 通过反射反射出所有插件程序集中的插件并维护一个插件列表 => 通过反射调用插件中的约定命名的方法注册插件信息 => 通过反射“特性”或者特定命名的属性的匿名类型的属性值并使用上一步注册的模板显示数据。

下图:Oxite2中已有的Xbox Live Gamercard插件的效果



说明:可以在后台直接编辑cs源代码,重新编译代码文件,重新加载插件程序集,重新加载插件,应用程序不必重启。

1
0
(请您对文章做出评价)
« 上一篇:浅谈Oxite2的数据访问和数据验证
» 下一篇:总结一下领域模型的验证:附代码下载
Add your comment

23 条回复

  1. #1楼 Astar      2009-09-22 05:58
    我们离的最近,所以我要第一个支持!呵呵,加油!
      回复  引用  查看    
  2. #2楼 xland      2009-09-22 08:13
    我们认识,我要第二个支持!(我本想第一个支持的...)
      回复  引用  查看    
  3. #3楼 Old      2009-09-22 08:43
    条理清晰,通俗易懂!
      回复  引用  查看    
  4. #4楼 Old      2009-09-22 08:49
    BuildManager.GetCompiledAssembly(string virtualPath)
    在MSDN 2005 简体中文版里的解释为:
    “将虚拟路径所表示的文件编译为程序集。 ”
      回复  引用  查看    
  5. #5楼 Smalltalk      2009-09-22 09:11
    oxite怎么用啊!运行起来的添加文章看不到
      回复  引用  查看    
  6. #6楼[楼主] Xuefly      2009-09-22 09:45
    @Old
    @xland
    @Astar
    谢谢
      回复  引用  查看    
  7. #7楼[楼主] Xuefly      2009-09-22 09:52
    @Old
    @Old
    原来是这样啊
    Compiles a file into an assembly given its virtual path.
    估计英文原文上少了一个by,翻译出来的中文更难理解了:修改一下可能就好理解了
    Compiles a file into an assembly by given its virtual path.
      回复  引用  查看    
  8. #8楼[楼主] Xuefly      2009-09-22 09:53
    @Smalltalk
    用uid=admin;pwd=pa$$w0rd进入后台添加文章的
      回复  引用  查看    
  9. #9楼 Smalltalk      2009-09-22 10:08
    这个我知道,只是点add post以后 添加文章 点击save以后 在home页面中看不到我新添加的文章。你QQ号多少?
      回复  引用  查看    
  10. #10楼[楼主] Xuefly      2009-09-22 10:16
    @Smalltalk
    我刚刚试了,可以。发文章的时候默认选择的是保存为草稿,改成选择发布就看到了。^^
      回复  引用  查看    
  11. #11楼 Smalltalk      2009-09-22 10:29
    Slug isn't valid.
    slug是什么东西?该填写什么呢?
      回复  引用  查看    
  12. #12楼[楼主] Xuefly      2009-09-22 10:34
    @Smalltalk
    Slug就是我们自定义的尽量有意义的Url片段。Oxite的Slug不支持中文,如果标题是英文的话,Oxite会自动根据你的文章标题并用配置的字符(一般为"-")替换掉空格作为该篇文章Url的Slug。如果标题是中文需要自己填写Slug并且不能有空格(用分割字符代替)。
      回复  引用  查看    
  13. #13楼 Smalltalk      2009-09-22 10:44
    谢谢! 但是怎么能让它在home页中可见呢?而且除了写“-”之外,举个具体的例子吧!
      回复  引用  查看    
  14. #14楼 ·风信子·      2009-09-22 11:20
    支持支持
      回复  引用  查看    
  15. #15楼 Denni      2009-09-22 21:32
    太好了。盼了好几天了,支持,好好学习一下再讨论。
      回复  引用  查看    
  16. #16楼 龙月      2009-09-24 23:31
    郑州27区的,
    对于Oxite2 没研究过,之前玩过 winform 上的Assembly类并做过简单的插件程序,
    但是不知道有什么区别?
    一个是cs?一个用于BS?
    本人很菜,还有您说的
    Oxite2的插件机制是基于“约定”的。“契约”是必须履行的,“约定”是非强制履行的

    他这个非强制履行体现在哪?“约定”和“契约”区别是什么?

    望能回答,谢谢
      回复  引用  查看    
  17. #17楼[楼主] Xuefly      2009-09-24 23:53
    @龙月
    我对插件学习的不深,目前只了解Oxite2上的插件机制,所以第一个问题不好回答。
    “约定”是非强制性履行体现在:我们会多出一个检查步骤,检查“约定”的事情是否被旅行了。而“契约”不需要检查,接口签订的是“契约”如果签订的接口没有被履行的话根本就编译不过,所以我们不必检查有没有实现接口的成员了,因为必定是实现的。

    在Oxite2中各个插件(比如TwitterPlugin类)均没有继承自任何接口,在Oxite2的所有插件中都约定了具有如下几个方法RegisterTemplates,RegisterRoutes,RegisterScripts,RegisterStyles但是在反射并调用这些方法的时候会进行判断,判断约定的这些方法有没有被实现,也就是说“约定”插件具有这些签名的方法但是并不强制,只有实现了才会调用:
    public static object ExecuteMethod(this PluginContainer pluginContainer, string methodName, params object[] parameters)
            {
                MethodInfo method = pluginContainer.GetMethod(methodName);
    
                if (method != null)
                    return method.Invoke(pluginContainer.Instance, parameters);
    
                return null;
            }
    

    另外在Oxite2的插件中插件的属性命名约定为以“Definition”后缀结尾,比如UsernameDefinition,用于反射。
      回复  引用  查看    
  18. #18楼 龙月      2009-09-25 20:10
    谢谢您的回答,呵呵
      回复  引用  查看    
  19. #19楼 我是一只小老虎 喵!      2009-11-02 22:00
    楼主怎么看待Oxite 1.0呢,我觉得它贼烂,你是怎么看的呢?

    乱七八糟的东西揉在一起.看得很讨厌呀.2.0是否会好一点?
      回复  引用  查看    
  20. #20楼 LevinLee      2009-12-27 03:53
    用到的知识点不少
      回复  引用  查看    
  21. #21楼 EricZhang(T2噬菌体)      2010-01-13 00:44
    学习了!刚好最近在研究插件机制
      回复  引用  查看    
  22. #22楼 a-peng      2010-01-13 22:33
    Hi 楼主我在看Oxite代码时遇到些问题。不知道能否Helper下。

    在Core项目的OxiteModules中有设置ControllerFactory

    ControllerBuilder.Current.SetControllerFactory(container.Resolve<OxiteControllerFactory>());

    可是我找不到有Register这个OxiteControllerFactory

    在OxiteControllerFactory代码中
    protected override IController GetControllerInstance(Type controllerType)
    {
    IController iController = container.Resolve(controllerType) as IController;
    找Controller的时候,也只是看到Resolve找不到Register Controller的相关代码,那这些Controller构造函数中的Service是在哪里被注入的呢?
      回复  引用  查看    
  23. #23楼 Denni      2010-01-16 12:23
    @博主:
      你好,关于该插件应用,我还有一个问题请教一下:BuildManager.GetCompiledAssembly这个方法可以对一个CS方法编译成DLL,如果我想把多个CS编译成一个DLL如何操作?或是又想把plugins文件夹编译成一个DLL又如何呢。谢谢,盼复。
      回复  引用  查看