DotText:.Text彻底剖析

作者:韩磊

一、序篇

    CoDelphi Blog使用的,是一个名为“.Text”的基于 ASP.NET 的开源应用程序。

    Scott Water开始写这个应用的时候,一定没有想到,他的软件在中国会得到很好的应用,更加不会想到在用.Text构筑的Blog社群之间,居然会平地起风波,发生一段纠纷。这个略去不提。

    就我所知,国内至少有三个基于.Text的中文版本。一个是开心就好的0.94汉化版,另外两个,一个是dudu的、一个是CoDelphi团队的0.96版本。开心就好的版本,大体而言只是汉化了UI,而dudu和CoDelphi的版本,则加入了许多的特性和功能。我无意批评开心就好的版本,毕竟那是第一个汉化版,而且在博客堂运行得很好。

    dudu是扬州人,他用自己的汉化版本搭建了“博客园”社区,而且一直不断地在为博客园添加新特性。我跟dudu认识于ASP.NET网站的.Text论坛,最近才有缘得见这位专注、执着的朋友。

    因为与dudu同样的对.Text本身的热爱,我自己的Blog在从Blogger.com撤离之后,一直就是用.Text,只是简单汉化了一下外面的UI,管理界面还是英文的。后来,由于要给donews搭blog,才打算做一个汉化增强版本。

    这个版本最初是由bonycamel和virushuo汉化,跟着我接手过来,理了一遍,也加了许多功能。其间,大鱼儿一直在提供帮助。到今天为止,这个版本已经在我的BlogCoDelphiCSDN内部Blog正常运行。我不想对上面提到的这些人们说感谢,因为我们没有主客之分。从另外一个方面来说,.Text本身是一个开源的项目,Scott才是最值得竖大拇指的人。Scott,谢谢你,尽管你的.Text 0.96还有许多不足,我们仍然要对你致以敬意。是你让我们可以近乎自由地写作。

    在将来某一时刻,我会把自己修改、添加的代码开放出来,但不是现在,因为我觉得它还需要完善。现在我要做的,是试图通过一系列文章,看看能否把.Text整个架构和一些具体细节谈上一谈。一边总结前段时间玩.Text的经验教训,另一边也希望能为喜爱.Text的人们提供一些有用的资料。

    我生性懒惰,靠自己一定坚持不下去。各位看官,如果你觉得文章还值得一读,请督促我继续。

二、资源篇

    在正式开始系列文章之前,向读者介绍相关资源是必要的。我将先入为主地认为读者都是聪明的程序员或是编程爱好者,懂得从下面的资源列表中找到自己需要的内容——包括.Text源代码、安装指引、基本操作等等。如果读者真的找不到,那么,可能存在两种情况:

  1. 下列资源中根本没有你要找的内容;
  2. 也许你不够仔细。

    对于第一种情况,我会视乎读者的要求,在后面的文章中尽量加以阐释,或者个别回复;至于第二种情况嘛,你可以写信或是在文章后面留言,告诉我你的困惑。北京读者请备啤酒数支,以便我笑纳。:) 当然,很多问题我也不见得能明白,所以,请保持对我胡掰的高度警惕性。

    请读者务必查看以下资源:

  1. Scott Water(.Text原作者)的Blog:http://scottwater.com/blog
  2. .Text Wiki(安装、使用问题请到这里寻找):http://dottextwiki.scottwater.com/
  3. ASP.NET上的.Text专题论坛:http://www.asp.net/Forums/ShowForum.aspx?tabindex=1&ForumID=149
  4. 源代码:原来是在GotDotNet Workspace,现在已经搬迁到另外一个服务器,使用Vault版本控制软件。在这个地址可以查看详情和操作指引:http://dottextwiki.scottwater.com/default.aspx/Dottext.SourceCode

    O.K.,下面一篇文章,我们开始进入.Text世界。

三、安装

    现在你应该已经从Vault服务器得到源代码,并且编译好所有项目。然而,安装.Text并不是件轻而易举的事情,尤其是对于中文地区使用者而言更是如此。

    在安装过程中,会有哪些障碍呢?

  1. IIS配置。由于IIS的愚蠢,我们需要做一些额外的工作。
  2. 数据库。Scott提供的SQL查询文件有一些错误;中文、英文之间的兼容性问题,也会让用户陷入迷魂阵中。
  3. Web.config配置文件。.Text Wiki没有完整地说明web.config文件应该如何配置;对于扩展特性,需要额外的解释。

    没错,就是这么麻烦。在谴责.Text原作者的“敬业”之前,请理解Scott独立业余开发.Text的难度——既然Scott已经通过BSD协议开放了.Text的一切,作为受益者的我们,怎能贪得无厌、得寸进尺要求他奉献更多呢?

    不过没关系,Scott没有告诉大家的,我来告诉;Scott没有说明白的,我来说明白;Scott的小失误,我也会尽量来修正它们。这就是本节的内容:在安装.Text的过程中,会遇到什么问题?怎么解决?

    想想看,安装.Text应该有哪几个步骤呢?对,先设置IIS,然后是数据库,最后轮到web.config配置文件。我们一个一个来看。

设置IIS

    .Text可以在站点根目录运行,也可以安装到一个虚拟目录。非常不行,在Wiki中提到“使用UseWWW来允许类似www.sitename.com这样的URL”,是行不通的,Scott在代码中使用了太多硬干的方式,导致这一特性无法彻底实现。我将在后面的章节中说明如何解决这个问题,所以,请暂时忍受没有www的不便。

    我在本地设置了一个新站点,站点头为test.com(这个域名仅在本地有用,是在hosts文件中设置的),根目录为DottextWeb。.Text支持两种形式的Blog:多用户Blog和单用户Blog。对于其中任何一种形式,都可以使用根目录或虚拟目录的安装方式。不过,就多用户Blog而言,有一个问题却是必须注意的。

    在多用户Blog系统中,每个用户的访问目录被定义为“Blog根目录/用户名”的形式。例如,在test.com上有一个名为hanlei的用户,则该用户的Blog访问地址为 http://test.com/hanlei 。想到是什么问题了吗?在通常的WEB应用中,这样的URL意味着在站点test.com根目录下,必须存在一个物理目录:hanlei。在访问http://test.com/hanlei 的时候,实际上是在访问该目录下的默认页面(通常是default.aspx)。问题就在于此。如果一个多用户站点拥有10万个用户,是不是也需要创建10万个子目录呢?这10万个子目录中的default.aspx页面,又是怎么创建的呢?

    最简单的方法自然是用System.IO提供的方法直接创建目录和一个空白的default.aspx文件,让.Text自己去处理剩下的事。不过这种方法显得有些dirty——10万子目录的文件夹,怎么说都是不堪入目的;再者,还牵涉到I/O操作性能问题……难道没有其他办法可以解决问题吗?

                  private void CreateDir (string DirPath)
                {
                   DirectoryInfo dirInfo=new DirectoryInfo(DirPath);
                   if(!dirInfo.Exists)
                   {
                      try
                      {
                          dirInfo.Create();
                          FileInfo fileInfo = new FileInfo(DirPath + "/default.aspx");
                          FileStream fs = fileInfo.Create();
                          fs.Flush();
                          fs.Close();
                      }
                      catch
                      {
                          throw;
                      }
                   }
                }

代码段一 创建子目录和空白的default.aspx

    办法自然是有的。其关键点还是在default.aspx文件上。真是成也萧何,败也萧何。想想看,为什么空白的default.aspx能显示出用户Blog文章内容?要知道,这个default.aspx是在运行时用代码创建,其中没有任何内容,更加说不上代码了。原因只有一个:用户目录下的空白default.aspx,只是诸葛亮草船上借箭的草人!

 
图一 一个Http请求是如何被处理的

    创建用户目录和空白默认页面的目的,就是欺骗IIS,让它以为可以放心地把请求交给ASP.NET处理了——嗯,有默认页面default.aspx,没问题,接手吧。此时,.Text Url Rewriting Handler插手了。而default.aspx,则根本就是斗牛士手中的那块红布,公牛向红布直冲过来时,迎接它的却是一柄利剑。

    除了dirty地创建不会被访问的default.aspx外,还有一种方法可以阻止IIS狗拿耗子多管闲事,那就是直接让ASP.NET处理一切请求,架空IIS缺省处理程序。做法很简单,在主目录的应用程序设置中,添加aspnet_isapi.dll映射,让它处理全部动作(使用通配符*)。

 
图二 设置IIS

设置数据库

    在源代码的OtherStuff/Sql Scripts/096目录中,包括了建立数据库需要的所有SQL查询文件。

四、汉化篇

    好了,好了,我已经听到一些读者强烈要求把.Text源代码下载、安装等等内容统统明白地讲清楚。是啊,我的确在前头说过,此类问题不宜赘述;但读者即上帝,上帝一生气,作者就惨了——该怎么办才好呢?妥协一下,在下一篇文章中,回过头来谈那个问题吧。

    那么,这次从什么地方开始好呢?

    回忆一下,探究.Text的奥妙,是从汉化它开始的。而汉化的过程,也是一波三折,颇有值得一提的地方。不妨就从这里开始吧。汉化简单吗?简单。汉化简单吗?不,一点儿也不简单。通过貌似简单的汉化工作,能让你顺藤摸瓜,了解软件的整体结构。

    所谓汉化,其实是有数种方式。有直接从源代码汉化的,有汉化资源文件的,还有直接提取EXE、DLL中的字符串进行汉化的。其中,尤以源代码级的汉化最为困难、效果最为完美、也最易从中学到东西。为什么这么说呢?看看市面上流行的汉化版软件就可以知道,但凡是直接提取EXE、DLL中字符串进行汉化的,多半都会出现不中不洋、怪里怪气的说法。从资源文件(这里指类似XML配置文件等等文本文件)汉化的,效果虽好,无奈很多软件并不单独提供语言资源文件;即便有,也无助于你深入探索软件本身的架构,对于好奇心强的我们来说,也太简单了一点。唯有源代码级的汉化,才是最彻底、最能体现实力、最有意思的汉化工作。

     .Text提供了完整的源代码,给我们进行完美汉化提供了极大的方便。我假设读者已经下载了.Text代码,并且安装好。为了便于讲解,我设置了www.test.com域名,指向本机的DotTextWeb目录,该目录位于DotText文件夹下,是.Text的Web根目录。(不要问我怎么设的,改一下hosts文件就可以了)

    你可以使用记事本来进行下面的工作,但我并不建议这么做。VS.NET提供了良好的导航界面和编辑器、还有集成的编译平台。用它来打开工程,事情会简单许多。你需要修改Dottext的sln和DotTextWeb的webinfo文件,使之指向http: //localhost。记得在IIS中把DotTextWeb同时指定为www.test.com和localhost的目录。用VS.NET打开Dottext.sln解决方案。

    现在你可以在解决方案资源管理器中看到.Text的几个组成部分。

 

    整个.Text由7个项目组成,其中,前六个是最主要的。而DottextWeb项目,就是位于最外面的Web表现层。汉化,自然要从DottextWeb开刀。

五、架构篇

以下是我个人对DotText的数据访问模式的一点看法,提出来供大家批评指正 --fallseir at 20040903


DotText数据操作结构


config BlogProviders DTO Entitys、DTOProvider、IDTOProvider、DTOProviderConfiguration;DataDTOProvider DB Operation Entitys、BaseDatas、DbProvider、IDbProvider、DbProviderConfiguration DBMS DBEntitys、DBMS


config


BlogProviders提供所有动态配置信息

        包括正确实例IDTOProvider和IDbProvider的信息

DTO


Entitys 业务实体集,用于存储上一层所需要的实体信息 可序列化 IDTOProvider 提供对Entitys中的业务实体对象的各种操作 --为可配置服务-- DTOProvider 使用BlogProviders中获得DTOProviderConfiguration实例

        通过DTOProviderConfiguration获得IDTOProvider的正确实例

DTOProviderConfiguration 对应于BlogProviders中的DTOProviderConfiguration节包含有外部配置的DTOProvider信息 --DataDTOProvider实现-- DataDTOProvider IDTOProvider的默认实现通过DbProvider获取IDbProvider的唯一实例,并调用相应的接口方法完成相应的功能使用DataHelper将复杂的业务实体从IDbProvider的返回值映射到相应的业务实体


DB Operation


Entitys 业务实体类作为操作的参数传给IDbProvider BaseDatas 基本数据类型作为操作的返回值传递到上一层 IDbProvider 提供数据操作的实现接口 --为可配置服务-- DbProvider 使用BlogProviders中获得DbProviderConfiguration实例

        通过DbProviderConfiguration获得IDbProvider的正确实例

DbProviderConfiguration 对应于BlogProviders中的DbProviderConfiguration节包含有外部配置的DbProvider信息 --SqlDbProvider实现-- IDbProvider的Sql Server默认实现为SQLHelper提供可用参数和操作命令并调用SqlHelper进行数据操作


DBMS


DBEntitys 实体表数据的保存位置,基本数据操作proc


数据操作描述 以DataDTOProvider的GetUser为实例描述


当业务层欲获得BlogUser的信息时

    通过DTOProvider的Instance()获取配置信息中DTOProvider指定的DataDTOProvider对象
    通过指定的UserName调用IDTOProvider的GetUser获取BlogUser对象
    BlogUser对象中包含了需要的BlogUser的信息
    -------------------------------------
    获取配置信息中DTOProvider指定的DataDTOProvider对象
    在DTOProvider的static构造函数中通过Config.Settings.BlogProviders.DTOProvider指定的DTOProviderConfiguration对象实例化一个唯一的IDTOProvider对象
    在需要时可以通过DTOProvider的静态方法Instance()获取IDTOProvider对象的引用

调用DataDTOProvider的GetUser获取BlogUser对象通过DbProvider的Instance()获取配置信息中DbProvider指定的SqlDataProvider对象

    通过指定的UserName调用IDbProvider的GetUser获取包含BlogUser信息的IDataReader对象
    通过DataHelper.LoadUserFromReader()将传入的IDataReader对象映射为BlogUser对象
    BlogUser对象中包含了需要的BlogUser的信息

获取配置信息中DbProvider指定的SqlDataProvider对象同获取DataDTOProvider对象相似的方式获取IDbProvider的实例SqlDataProvider对象在DbProvider的static构造函数中通过Config.Settings.BlogProviders.DbProvider指定的DbProviderConfiguration对象实例化一个唯一的IDbProvider对象

    在需要时可以通过DbProvider的静态方法Instance()获取IDbProvider对象的引用。
posted on 2005-01-14 14:55  caizi  阅读(2254)  评论(5编辑  收藏  举报