DotText源码阅读

[转载] DotText源码阅读(7) --Pingback/TrackBack
from: http://blog.csdn.net/shanhe/archive/2006/05/17/741904.aspx
博客这种服务的区别于论坛和所谓文集网站,很大程度上我认为是由于pingback/trackback的存在,使得博客这种自媒体有可以延展加入SNS的要素。所以分析博客程序,我们需要了解这种协议以及协议的实施细节。
       在dottext的源代码中,在发表作品中,我们可以看到有pingback协议的支持,同时在web services的实现中,有trackback协议的实现。至于什么是piongback/trackback协议,google下应当可以找到,也不用我费口舌。
              通过              <HttpHandlerpattern="/(?:admin)"type="Dottext.Web.UI.Handlers.BlogExistingPageHandler, Dottext.Web"handlerType="Factory"/>
的映射,使得我们访问每一个blog的admin目录时候,都会UrlRewrite到dottexweb\admin目录下的相对应aspx文件(参考前面部分),其中在发表post的时候,我们看到是这样一个调用关系:
       private void UpdatePost()
         {   
              if(Page.IsValid)
              {
                   string successMessage = Constants.RES_SUCCESSNEW;
                   try
                   {
                       Entry entry = new Entry(EntryType);
 
                        entry.Title = txbTitle.Text;
                        entry.Body = Globals.StripRTB(ftbBody.Text,Request.Url.Host);
                      
                        entry.BlogID = Config.CurrentBlog(Context).BlogID;
                      
                       if (PostID > 0)
                       {//是更新操作
                            successMessage = Constants.RES_SUCCESSEDIT;
                            entry.DateUpdated = DateTime.Now;//BlogTime.CurrentBloggerTime;
                            entry.EntryID = PostID;
                           
                            Entries.Update(entry);
                           
                       }
                       else
                       {//新建操作
                            entry.DateCreated = DateTime.Now;//BlogTime.CurrentBloggerTime;
                            PostID = Entries.Create(entry);       
                       }
                   }
                   catch(Exception ex)
                   {           }
                   finally
                   {            }
              }
     }
     Entries.Create(entry);是这样的:
         public static int Create(Entry entry, int[] CategoryIDs)
         {
              HandlerManager.PreCommit(entry,ProcessAction.Insert);            
              int result = DTOProvider.Instance().Create(entry,CategoryIDs);             
              if(result > 0)
              {
                   HandlerManager.PostCommit(entry,ProcessAction.Insert);                
              }
              return result;
     }
     最终的数据存储试调用DTOProvider也就是DataDTOProvider 最终是落到 SqlDataProvider 来实现数据存储操作。但是我们注意到 HandlerManager.PostCommit(entry,ProcessAction.Insert);     这个操作。仔细看看:
     HandlerManager 是一个关于Entry操作类的包装类(wapper class),PreCommit是这样定义的:
          Process(ProcessState.PreCommit,e,pa);
     而Process是这样读取web.config的
     public static void Process(ProcessState ps, Entry e, ProcessAction pa)
         {     //Do we have factories? 在疑惑是否该用工厂模式呢
              EntryHandler[] hanlers = Config.Settings.EntryHandlers;     //这是反序列化哦,这里的Config是Dottext.Framework.Configuration.Config
              if(e != null && hanlers != null)
              {     //walk the entries 遍历全部处理例程
                   for(int i = 0; i
                   {
                        EntryHandler handler = hanlers[i];
                        if(ShouldProcess(ps,handler,e.PostType,pa))
                       {
                            IEntryFactoryHandler ihandler = handler.IEntryFactoryHandlerInstance;                           
                            //Call the IEntryFactoryHandler configure method. This gives async items a chance to "ready" themselves
                            //before leaving the main thread and entering the managed queue.
                            ihandler.Configure();
                            if(handler.IsAsync)
                            {//Add factory to managed queue.
                                 EntryHanlderQueue.Enqueue(ihandler,e);
                            }
                            else
                            {
                                 ihandler.Process(e);
                            }
                       }
                      
                   }
              }
         }
ShouldProcess 是判断是预提交还是已经提交post,决定是否应该进行handler的实例化,如果是已经提交的Post,我们需要进行handler.IEntryFactoryHandlerInstance;      IentryFactoryHandlerInstance最终是通过
ihandler = (IEntryFactoryHandler)Activator.CreateInstance(Type.GetType(this.ItemType));
来实例化数组元素的().
经过实例化后,就可以执行了。此时根据 handler.IsAsync 的属性,决定是允许 EntryHanlderQueue.Enqueue(ihandler,e); 加入队列,还是马上处理
ihandler.Process(e);.
对于可以异步执行的静态函数 Enque 处理:
public static void Enqueue(IEntryFactoryHandler factory, Entry e)
         {
              EntryHanlderQueue ehq = new EntryHanlderQueue(factory,e);
              ManagedThreadPool.QueueUserWorkItem(new WaitCallback(ehq.Enqueue));
     }
构造一个实例,然后加入线程队列进行任务排队。线程管理暂不讨论。我们看看这几个EntryHandler.
TrackBack Handler是如何处理的呢?
public void Process(Dottext.Framework.Components.Entry e)
         {
              //Get a list of links from the current post
              StringCollection links = TrackHelpers.GetLinks(e.Body);
              if(links != null && links.Count > 0)
              {
                   //Instantiate our proxy
                   TrackBackNotificationProxy proxy = new TrackBackNotificationProxy();
                  
                   //Walk the links
                   for(int i = 0; i
                   {
                       string link = links[i];
                       //get the page text
                       string pageText = BlogRequest.GetPageText(link,e.Link);
                        if(pageText != null)
                       {
                            try
                            {
                                 string desc = null;
                                 if(e.HasDescription)
                                 {
                                     desc = e.Description;
                                 }
                                 else
                                 {
                                      desc=string.Format("TrackBack From:{0}",e.Link);
                                      
                                 }   
desc = regexStripHTML.Replace(e.Body,string.Empty);
                                      if(desc.Length > 100)
                                     {
                                          int place = 100;
                                          int len = desc.Length-1;
                                          while(!Char.IsWhiteSpace(desc[place]) && i < len)
                                          {
                                               place++;
                                          }
                                          desc = string.Format("{0}...",desc.Substring(0,place));
                                     }
                                 }
                                 //attempt a trackback.
                            proxy.TrackBackPing(pageText,link,e.Title,e.Link,e.Author,desc);                           
                            }
                            catch(Exception ex)
                            {                                 Logger.LogManager.CreateExceptionLog(ex,string.Format("Trackback Failure: {0}",link));
                            }
                       }
                   }
              }
     }
 TrackHelpers.GetLinks 会分析Entry.Body字符串,获得post的全部href连结,也就是对外引用部分,这个TrackBack利用proxy.TrackBackPing(pageText,link,e.Title,e.Link,e.Author,desc); 将本文的对外引用通告刚刚获得的连接地址。
      
TrackBackPing :
    string pageText = BlogRequest.GetPageText(link,e.Link);会利用BlogRequest的http协议能力下载被引用地址的source code,然后 link为另外blog的地址,而e.Link为reffer,这是为了告知对方那个页面引用了link。经过安全解码后,获得了link的源代码,然后TrackBackPing会进行分析,找寻string sPattern = @"]*?>()?";匹配的部分,分析出其中的引用通告地址。下一步就是利用SendPing(string trackBackItem, string parameters),向目标地址处post一个application/x-www-form-urlencoded"的数据。此即完成了一次trackBack.
  其他几个EntryHandler也是分同步和异步的,大家可以照此阅读。
    
    题外话:那些没有礼貌的实现pingback/Trackback的所谓blog,就不要妄自称自己为博客服务商(BSP)吧。
posted @ 2006-07-12 17:39 pack27 阅读(31) | 评论 (0) |  编辑  收藏

[转载] DotText源码阅读(6) --模版皮肤
from: http://blog.csdn.net/shanhe/archive/2006/05/13/727211.aspx
我们看到,dottext的个人模版是可以替换的,但显然,这么多套模版不可能每一套都写一个包含界面的程序文件。dottext的作者采用了UrlRewrite来实现模版和换肤,我们分析其原理后,可以明确如何做到为blog增添新的模版,实现站点blog的个性化。说到这个性化,可是web2.0的命根子!
就以本人的blog为例来说明吧:
假设我们访问 http://blog.csdn.net/shanhe ,http请求会经由IIS传给asp.net的进程(通过管道交互数据),而blog.csdn.net这是一个应用程序,web.config起作用了,它的请求由
<addverb="*"path="*"type="Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common"/>
而被UrlReWriteHandlerFactory 接管。在该工厂处理例程中,会取出当前请求的URL:
string appStr=Dottext.Framework.Util.Globals.RemoveAppFromPath(context.Request.Path,context.Request.ApplicationPath);
此时得到的 context.Request.Path 为 /shanhe 而 Request.ApplicationPath 为 “/”经过替换后的到 appStr为 “/shanhe”,这个同
<HttpHandlerpattern="^(?:\/(\w|\s|\.(?!aspx))+((\/default\.aspx)?|(\/?))?)$" controls="homepage.ascx"/>
指定的pattern 相匹配。此时的HandlerType为Page,自然执行到
case HandlerType.Page://默认是Page
          return ProccessHandlerTypePage(items[i],context,requestType,url);
       我们再进入ProccessHandlerTypePage
     private IHttpHandler ProccessHandlerTypePage(HttpHandler item, HttpContext context, string requestType, string url)
         {     string pagepath = item.FullPageLocation;
              if(pagepath == null)
              {
                   pagepath = HandlerConfiguration.Instance().FullPageLocation;
              }
              HandlerConfiguration.SetControls(context,item.BlogControls);
              IHttpHandler myhandler=PageParser.GetCompiledPageInstance(url,pagepath,context);
              return myhandler;
         }
item为匹配的handler的FullPageLocation,通过阅读 handler的实体类可以看到属性:
private string _fullPageLocation;
         public string FullPageLocation
         {
              get
              {     if(this._fullPageLocation == null && PageLocation != null)
                   {
                        this._fullPageLocation = HttpContext.Current.Server.MapPath("~/" + PageLocation);
                   }
                   return this._fullPageLocation;
              }
     }
这里的fullPageLocation没有指定,为null,进入后执行
          pagepath = HandlerConfiguration.Instance().FullPageLocation;
     此处又有反序列化,会进入HandlerConfiguration的静态函数Instance:
     public static HandlerConfiguration Instance()
         {
              return ((HandlerConfiguration)ConfigurationSettings.GetConfig("HandlerConfiguration"));
         }
     此时pagepath得到了值为web.cofig中指定的 defaultPageLocation="default.aspx",由于
this._fullPageLocation = HttpContext.Current.Server.MapPath("~/" + PageLocation);
     会将缺省文件定位到blog站点的实际应用程序路径跟目录下的deafault.aspx,可能形如:
"X:\CNBlogsDottext\DottextWeb\default.aspx",asp.net会通过 PageParser.GetCompiledPageInstance(url,pagepath,context);执行这个文件。
由此时开始,进入了skin的执行阶段。
 
Default.aspx很简单,仅仅包含了数个嵌套的容器控件
         
             
                  
                      
                  
             
MASTERPAGE的源代码(没有UI部分) 在dottextweb\ui\webcontrols\下,需要阅读的是该控件的属性
public string TemplateFile {
              get
              {
                   if(this.templateFile == null)
                   {
                        BlogConfig config=Config.CurrentBlog(Context); //读取blog配置,根据
                        if(config!=null)
                       {
                            this.templateFile = config.Skin.TeamplateFilePath;
                       }
                   }
                   return this.templateFile;
              }
              set { this.templateFile = value; }
         }
通过BlogConfig config=Config.CurrentBlog(Context);   读取blog配置,涉及到DTO部分,请参考前面的阅读。反正是取出来了当前我们要访问的blog的全部配置信息,将skin名称获取到。
 
 
     此时我们要开始进入页面生命周期的分析了:
     通过实例化Page对象后,即进入了初始化阶段(Initialize),会调用Page_Onint事件并相应进入各个也面控件的OnInit方法,并递归到子控件的OnInit。Default.aspx主要控件是MASTERPAGE,此控件的OnInit是这样的:
          protected override void OnInit(EventArgs e) {
              this.BuildMasterPage();
              this.BuildContents();
              base.OnInit(e);
         }
     分别执行 加载子控件(BuildMasterPage)的过程和生成子控件实例(BuildContents)的过程:
          private void BuildMasterPage() {
              if (this.TemplateFile == null || this.TemplateFile == string.Empty) //取得模版名称,如果不存在或者未指定那么就大祸了,抛出一个例外吧
              {
                   throw new ApplicationException("TemplateFile Property for MasterPage must be Defined");
              }
              this.template = this.Page.LoadControl(this.TemplateFile);
              this.template.ID = this.ID + "_Template";
              int count = this.template.Controls.Count;
              for (int index = 0; index < count; index++) {        //注意 收集的遍历删除代吗
                   Control control = this.template.Controls[0];
                   this.template.Controls.Remove(control);
                   if (control.Visible) {
                        this.Controls.Add(control);
                  
                   }
              }
              this.Controls.AddAt(0, this.template);
         }
`分析class SkinConfig不难得到 this.Page.LoadControl(this.TemplateFile);是指向skin目录下的某个子目录的PageTemplate.ascx,这个实际上才是模般的展示“舞台”。PageTemplate实际上包含了其所在目录的子目录controls目录中的大部分控件,在执行 this.template.ID = this.ID + "_Template";
 进行命名后,dottext又利用一个循环将全部的PageTemplate包含的子控件一一装载(当然是必须是可Visible的)。BuildMasterPage实际上模仿的就是asp.net的装载包含子控件的页面的内部细节。
随后的BuildContents则BuildMasterPage的到全部子控件进行加载到用于将 contentregion 控件(这是一个容器控件):
private void BuildContents() {
              foreach (ContentRegion content in this.contents) {
                   Control region = this.FindControl(content.ID);
                   if (region == null || !(region is ContentRegion)) {
                       throw new Exception("ContentRegion with ID '" + content.ID + "' must be Defined");
                   }
                   region.Controls.Clear();
                   int count = content.Controls.Count;
                   for (int index = 0; index < count; index++) {
                        Control control = content.Controls[0];
                        content.Controls.Remove(control);
                        region.Controls.Add(control);
                   }
              }
         }
加入的条件是在ContentRegion实例的现实范围内(刚刚提到的“舞台”)。
为什么要这样呢,因为this.Page.LoadControl(this.TemplateFile);这句话是装入一个用户控件,而用户控件自身其实是一个页面,那么为了整个最终给用户浏览到的页面的命名完整性,需要对全部控件的命名按照容器的规则重新命名,以免冲突。
其实主要的模版的执行过程在这个Page.LoadControl(this.TemplateFile),它会装入你在申请blog的时候特定模版下的模版用户控件,看看 config.Skin.TeamplateFilePath;的细节,原来TeamplateFilePath属性其实就是定义了需要装入的用户控件"PageTemplate.ascx" ,只不过选择的模版不同,具体的模版路径不同,PageTemplate.ascx也就不同了,这样就实现了“换肤”。
     PageTemplate.ascx主要都由PageTemplate.ascx所在目录下的子目录Controls下的一些用户控件组成,实现组成的到用户的UI部分。这些UI控件。我们知道,用户控件类似Page有2个文件组成,一个负责UI,由各种html组成,另一个文件是cs文件,执行用户控件的逻辑,并且常规情况下,他们在同一个目录下。但是在模版的实现中,dottext将UI文件(.ascx)放在不同的模版下面,而代码文件全部指向dottext 的web目录下面的UI\Controls\*.cs。这样,同一套逻辑(.cs文件)对应了N套UI的ascx文件,只要我们设定不同的skin的模版名称,就实现了自由切换模版,达到“换肤”的目的。
     那知道了这个原理,我们可以在模版开发上做到两个方向发展:
1、   为当前的模版添加blog上的新功能,譬如计数器(目前的版本没有)。
a)        首先在web目录下面的UI\Controls中加入实现的逻辑功能的cs文件
b)        在各个skin目录中加入界面实现UI元素的ascx文件
c)        在 PageTemplate.ascx或者具体的某个控件中加入计数器。
2、   增加新的模版。
这需要我们:
a)        拷贝其中一个skin,改成我们需要的名字(dottext的模版名称不知什么原因,总觉得乖乖的)
b)        然后修改其中的各个用户控件,但是不要修改Inherits="Dottext.Web.UI.Controls.XXXXXX"语句(XXXXXX为某个控件名称)。这样将做到UI同逻辑的映射。
c)        将skin名加入到blog的管理后台配置选项中供用户选择。
 
以上分析了模版的实现原理,可以帮助我们修改模版,在当前dottext设计基础上扩展新的界面元素,充分实现个性化。对于web2.0的主要服务之一博客来说,个性化相当重要。
posted @ 2006-07-12 17:38 pack27 阅读(48) | 评论 (0) |  编辑  收藏

[转载] dotText源码阅读(5)--URLreWrite和Handler
from:  http://blog.csdn.net/shanhe/archive/2006/05/10/722389.aspx
           Dottext需要映射全部不存在的文件到blog应用程序,实际上是需要IIS对于该应用下的问不进行处理,而是交给dottext程序处理,而dottext则利用一系列的handler来进行配置,对应不同的文件类型,或者匹配特定的文件,实现整个blog的URL 重写的。
        首先,是通过
<httpHandlers>
<addverb="*"path="*.asmx"type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"validate="false"/>
              <addverb="*"path="Error.aspx"type="System.Web.UI.PageHandlerFactory"/>
<addverb="*"path="*"type="Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common"/>
          httpHandlers>
确保了任何对blog所在应用程序的访问都会被以上3个handler处理,如果是扩展名.asmx的http请求,会被系统缺省的处理程序处理;而对于错误处理(大多数都是转到error.aspx)会转入到系统的缺省aspx处理程序,其他任何请求都会转到Dottext.Common.UrlManager.UrlReWriteHandlerFactory,Dottext.Common  所以我们首先来看看这个处理句柄:
这是一个工厂类型的执行句柄,他自身并不进行处理。而是负责将请求根据不同的类别进行分别派遣,调出不同的处理程序进行执行,而这构成了dottext高效处理整个blog运行的精妙设计部分。
protected virtual HttpHandler[] GetHttpHandlers(HttpContext context)
         {
              return HandlerConfiguration.Instance().HttpHandlers;//这是个收集
         }
IhttpHandler接口的实现,用于返回处理http请求的全部句柄。而句柄配置在web.config中,所以dottext是这样获得全部handler的。而HandlerConfiguration.Instance()类似我们前面分析的配置处理体系那里的处理过程:
public static HandlerConfiguration Instance()
         {
              return ((HandlerConfiguration)ConfigurationSettings.GetConfig("HandlerConfiguration"));
         }
是从配置文件的xml片断中,获得产生具体的类实例,并经过反序列化后(请看看HandlerConfiguration的属性定义),得到一个句柄的数组,返回给UrlReWriteHandlerFactory的调用函数。具体为:
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string path)
         {
              HttpHandler[] items = GetHttpHandlers(context);
              if(items != null)
              {
                   int count = items.Length;
                   string appStr=Dottext.Framework.Util.Globals.RemoveAppFromPath(context.Request.Path,context.Request.ApplicationPath);//得到访问哪一个应用程序的哪一个具体文件
                   for(int i = 0; i
                   {
                       //定向到特定的aspx文件
                        if(items[i].IsMatch(appStr))     //看看是否匹配系统配置中的者则表达式
                       {
                            //throw new Exception();
                            switch(items[i].HandlerType)
                            {
                                 case HandlerType.Page://默认是Page
                                     return ProccessHandlerTypePage(items[i],context,requestType,url);
                                 case HandlerType.Direct:                                   HandlerConfiguration.SetControls(context,items[i].BlogControls);
                                     return (IHttpHandler)items[i].Instance();
                                 case HandlerType.Factory:                                                                       return ((IHttpHandlerFactory)items[i].Instance()).GetHandler(context,requestType,url,path);
                                 default:
                                     throw new Exception("Invalid HandlerType: Unknown");
                            }
                       }
                   }
              }
              //如果请求的页面不匹配任何一个句柄,就使用ASP.NET的
              return PageHandlerFactory.GetHandler(context,requestType,url, path);
         }
获得全部web.config指定的handler以及正则表达式后,就进行匹配当前http访问请求的处理分析,如果请求的URL字符串匹配一个 Page类型正则表达式(HttpHandler是一个实体类,通过反序列化后获得了type和Pattern属性,如果请求的资源是aspx或者html的,那么会进行正则式判断是否符合句柄的模式,)如果符合,那么同时就知道了句柄的类型,根据HandlerType.Page 、HandlerType.Direct、HandlerType.Factory进行分别处理。
如果是Page类型(某个aspx页面),那么执行:
private IHttpHandler ProccessHandlerTypePage(HttpHandler item, HttpContext context, string requestType, string url)
         {
              string pagepath = item.FullPageLocation;
              if(pagepath == null)
              {
                   pagepath = HandlerConfiguration.Instance().FullPageLocation;
              }
              HandlerConfiguration.SetControls(context,item.BlogControls);
              IHttpHandler myhandler=PageParser.GetCompiledPageInstance(url,pagepath,context);
              return myhandler;
         }
HandlerConfiguration.Instance()的代码如下:
public static HandlerConfiguration Instance()
         {
              return ((HandlerConfiguration)ConfigurationSettings.GetConfig("HandlerConfiguration"));
         }
同样,这也是配置文件通过反序列化得到一个HandlerConfiguration的实例,HandlerConfiguration的配置节内容在web.config中存在,我们会得到一个defaultPageLocation属性,FullPageLocation如果在属性无法获取的时候就返回defaultPageLocation的值,也就是说,通常我们访问某个目录,不带指定的aspx 的page文件名,就会自动访问defaultPageLocation.的指示的值。
SetControls 是针对部分页面的,就是类似<HttpHandlerpattern="/archive/\d{4}/\d{1,2}\.aspx$"controls="ArchiveMonth.ascx"/>这类page 的,通常是指向一个用户控件,而大家知道用户控件实际上就是一个page。SetControls会把控件加入到当前请求的context重,以便执行期间从context中区的控件。
PageParser对象实际上是asp.net的解释对对象,它将指定的资源编译成程序集,这类似一个普通的物理存在的aspx页面执行机制。大家注意到,返回的是一个IhttpHandler对象,实际上asp.net的任何一个page都应该实现这个接口的,所以此处的逻辑就相当于执行了一个存在的页面。虽然页面可能不存在,但是通过配置指定最后得到了一个IhttpHandler对象处理了用户的http请求,这是Page类型的处理过程简要描述。
     第二类HandlerType是Direct ,有以下的http资源请求定向到这类Handler:
<HttpHandlerpattern="(\.config|\.asax|\.ascx|\.config|\.cs|\.vb|\.vbproj|\.asp|\.licx|\.resx|\.resources)$"     type="Dottext.Framework.UrlManager.HttpForbiddenHandler, Dottext.Framework"handlerType="Direct"/>
              <HttpHandlerpattern="(\.gif|\.js|\.jpg|\.zip|\.jpeg|\.jpe|\.css|\.rar|\.xml|\.xsl)$"type="Dottext.Common.UrlManager.BlogStaticFileHandler, Dottext.Common"handlerType="Direct"/>     
 ......               <HttpHandlerpattern="/services\/pingback\.aspx$"type="Dottext.Framework.Tracking.PingBackService, Dottext.Framework"     handlerType="Direct"/>               <HttpHandlerpattern="/services\/metablogapi\.aspx$"type="Dottext.Framework.XmlRpc.MetaWeblog, Dottext.Framework"     handlerType="Direct"/>
              可以看到,大部分我们找不到实际的文件名,但是却可以通过访问blog下的url返回内容,系统根据url判断如何返回内容。我们举一个例子来看看Direct怎么执行的。看看<HttpHandlerpattern="/rss\.aspx$"type="Dottext.Common.Syndication.RssHandler, Dottext.Common"     handlerType="Direct"/>
执行:
case HandlerType.Direct:                                   HandlerConfiguration.SetControls(context,items[i].BlogControls);
                                     return (IHttpHandler)items[i].Instance();
时候,会实例化一个Dottext.Common.Syndication.RssHandler类的实例(RssHandler是间接实现了IhttpHandler接口的),它继承自抽象类BaseSyndicationHandler,BaseSyndicationHandler实现了总体的返回特定格式RSS文档的功能和能力,通过继承覆盖,不同的格式的实现类(RSS20和ATOM等)实现了各自格式的rss文档返回给用户。总之,在这类的handler中,最终通过Context.Response操纵到客户的输出流。
     第三类的是Factory类型的,其自身就是一个工厂模式的handler,会再次将当前url转交给下一级handler,这样实现了可扩展性。如果dottext的新功能需要进一步处理URL得到其他功能就可以利用此类进行处理。譬如:
<HttpHandlerpattern="/(?:admin)"type="Dottext.Web.UI.Handlers.BlogExistingPageHandler, Dottext.Web"handlerType="Factory"/>
当用户访问应用程序下的/admin目录时候,自然处于该Dottext.Web.UI.Handlers.BlogExistingPageHandle 处理。由于是工厂模式,所以我们着重看看:
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string path)
         {     BlogConfig config = Config.CurrentBlog(context);
              if(ConfigProvider.Instance().IsAggregateSite)
              {
                   string app = config.Application.ToLower();
                   url = Regex.Replace(url,app,"/",RegexOptions.IgnoreCase);
app = "\\\\"+config.CleanApplication+"\\\\";
                   path = Regex.Replace(path,app,"/",RegexOptions.IgnoreCase);
                   if(!Regex.IsMatch(path,"\\.\\w+$"))
                   {
                       path = System.IO.Path.Combine(path,"index.aspx");
                   }
              }
              return PageParser.GetCompiledPageInstance(url, path, context);
         }
处理时候,首先取得当前blog的配置,ConfigProvider.Instance()返回一个Iconfig接口的实例,看看这个Instance的代码:
     static ConfigProvider()         //静态构造函数
         {
              ConfigProviderConfiguration cpc = Config.Settings.BlogProviders.ConfigProvider;
              config = (IConfig)cpc.Instance();
              config.Application = cpc.Application;
              config.CacheTime = cpc.CacheTime;
              config.Host = cpc.Host;
              config.ImageDirectory = cpc.ImageDirectory;
              config.BlogID = cpc.BlogID;             
         }
          private static IConfig config = null;
         public static IConfig Instance()
         {
              return config;
         }
执行静态构造函数,通过Config.Settings.BlogProviders.ConfigProvider反序列化得到ConfigProviderConfiguration。ConfigProviderConfiguration继承抽象类BaseProvider,通过BaseProvider的instance方法:
public object Instance()
         {
              return Activator.CreateInstance(System.Type.GetType(this.ProviderType));
         }
此处的ProviderType是通过反序列化得到:
[XmlAttribute("type")]
         public string ProviderType
         {
              get {     return _type;   }
              set { _type = value; }
         }
也就是
<ConfigProvidertype="Dottext.Common.Config.MultipleBlogConfig, Dottext.Common"host="localhost"     cacheTime="120"/> 中指明的MultipleBlogConfig 类型。MultipleBlogConfig继承自BaseBlogConfig , BaseBlogConfig实现了IConfig,所以你才看到
config = (IConfig)cpc.Instance();
然后,得到了相应的属性IsAggregateSite。该属性的意思是当前访问的是否是聚合站点(而不是但个博客的站点,只有聚合站点才可以使用存在的aspx文件)。确认聚合站点后,就取得应用程序目录下的实际aspx文件,然后利用CLR的功能PageParser.GetCompiledPageInstance(url, path, context)返回页面执行结果。
所有blog的http请求,依据URL通过正则表达式匹配到不同的Handler类型,实现了3种类别的处理,但最终用户看到的是请求执行结果。修改web.config我们可以进行特定资源的特殊执行,这是UrlReWrite的实质。
posted @ 2006-07-12 17:37 pack27 阅读(36) | 评论 (0) |  编辑  收藏

[转载] dotText源码阅读(4)--DTO和数据访问
from: http://blog.csdn.net/shanhe/archive/2006/05/06/709989.aspx
Dottext中的DTO是一个是怎么实现的呢?这些是作为3层体系的实现内容。DTO的使用很多高人都有自己的看法,争论也不少。不过,我在这里要说的是dottext为什么要用DTO,我理解作者是想通过DTO确保维护3层体系,目的是解耦合各层之间的相互依赖,为各层之间的更新升级预留足够的维护空间。
Dottext.Framework.Data. IDTOProvider 定义了关于DTO的接口,这个接口涉及到的对象机器操作有:
Entry        也就是blog中发表的文章,其实体、对象的声明在Components目录下的Entry.cs中。需要注意的是该类继承了IblogIdentifier 接口,并且声明了[Serializable]属性。几乎定义的实体类型都类似该类。
Links        收藏的链接
Categories        类别,注意blog的系统分类和每一个博客的分类一起存储的,通过-1的blog来区分系统定义的分类。
Stats               统计信息
Configuration     配置类
KeyWords               blog关键字
Images            相册
Archives         文章归档
ScheduledEvents        调度事件
Logger                   日志对象
Rate                点击统计
Security         身份验证
MailNotify        邮件
IblogIdentifier接口是规定了该类必须要有一个归属哪一个blogID的,这容易理解,因为无论是文章还是统计信息、个人连结、收藏、相夹都属于私人的。
除了实体类外,还实现了相应实体类的收集类。对于数据绑定来说,很多人喜欢采用实体收集类来代替DataSet等,我也是这类人。这些实体收集类也标记了[Serializable]属性,也可以利用序列化进行配置
 
关于这些实体类的操作,dottext定义了IDTOProvider的接口,来定义对DTO的操作,具体在Dottext.Framework.Data目录下,这个接口需要其他具体类实现,但是体现了作者的设计思路:就是不把实现根据体的数据层死死捆绑,这个可以为我们借鉴。
 
为了进一步实现自己的思路,dottext还特意夹了一个数据层的抽象,IDbProvider ,此接口实现了对于各个DTC实体的数据访问,但是都是通过定义返回IdataReader和DataSet来实现对于具体数据库的封装。Dottext的作者在书写代码时候,做了大量的分类注释。关于数据层的具体实现,我的这个版本是基于SQL server的,所有具体的数据操作在Data目录下的SqlDataProvider.cs,这个类实现了IdbProvider,但是我们看到的几乎都是存储过程调用,而dottext的Sql版本大约110多个,所以要仔细阅读这些数据层访问细节,会花费很多时间。但是理清了这些头绪,我们可以知道如何去阅读甚至去实现修改了。
 
。那系统是如何实现数据访问的灵活配置呢?这个举一个例子,发表文章的操作,最终的操作是落在admin\ UserControls\ EntryEditor.ascx上(详细分析可能后面会补充),其中的代码如下:
private void UpdatePost()
              {      
                     if(Page.IsValid)
                     {
                            string successMessage = Constants.RES_SUCCESSNEW;
                            try
                            {
                                   Entry entry = new Entry(EntryType);
                                   entry.Title = txbTitle.Text;
                                   ……
                                   entry.BlogID = Config.CurrentBlog(Context).BlogID;
                            if (PostID > 0)
                                   {
                                          successMessage = Constants.RES_SUCCESSEDIT;
                                          entry.DateUpdated = DateTime.Now;                                                                            entry.EntryID = PostID;                                      entry.Link=Dottext.Framework.Configuration.Config.CurrentBlog().UrlFormats.EntryUrl(entry);
                                          if(chkIsMoveTo.Checked)
                                          {
                                                 entry.PostType=entry.PostType^((PostType)3);
                                                
                                          }
                                          Entries.Update(entry);
                                          ……
 
                                   if (PostID > 0)
                                   {
                                          //LinkCollection lc = new LinkCollection();
                                          ArrayList al = new ArrayList();
                                          int count = cklCategories.Items.Count;
                                          if(chkIsMoveTo.Checked)
                                          {
                                                 count=0;
                                          }
                                          //文章分类
                                          for (int i =0; i
                                          {
                                                 if (cklCategories.Items[i].Selected)
                                                 {
                                                        al.Add(Int32.Parse(cklCategories.Items[i].Value));
                                                 }
                                          }
                                          //网站分类
。。。。。。
                            }
                            catch(Exception ex)
                            {                                   this.Messages.ShowError(String.Format(Constants.RES_EXCEPTION,
                                          Constants.RES_FAILUREEDIT, ex.Message));
                            }
                            finally
                            {
                                   Results.Collapsible = false;
                            }
                     }
              }
这里的Entry属于DTO类型,在 Components 下有解释。如果是第一次新发表的文章,那么会执行:
Entries.Update(entry);        
此时会执行:
public static int Create(Entry entry)
         {
              return Create(entry,null);
         }
静态方法,最终调用
public static int Create(Entry entry, int[] CategoryIDs)
         {
              HandlerManager.PreCommit(entry,ProcessAction.Insert);            
              int result = DTOProvider.Instance().Create(entry,CategoryIDs);             
              if(result > 0)
              {
                   HandlerManager.PostCommit(entry,ProcessAction.Insert);                
              }
              return result;
         }
我们主要集中看看
int result = DTOProvider.Instance().Create(entry,CategoryIDs);
该语句执行的需要好好揣摩:
DTOProvider 的声明在providers目录下:
他有一个静态的声明构造函数
static DTOProvider()
         {
              DTOProviderConfiguration dtoPC = Config.Settings.BlogProviders.DTOProvider;
              idto = (IDTOProvider)dtoPC.Instance();
     }
用于在静态调用该类的方法之前执行构造(这里相当于使用了单例模式)。此时会利用前民提到的配置体系获取DTOProvider配置。DTOProviderConfiguration 具 [XmlRoot("DTOProvider")]属性,从节获得的XML片断中得到DTOProviderConfiguration,而需要注意这里的DTOProviderConfiguration继承自一个抽象类BaseProvider。
Config.Settings.BlogProviders 通过反序列化得到了具体的BlogProvider类,但是我们仅仅想获取DTOProvider的属性,而在我手中的版本该处的配置是Dottext.Framework.Data.DataDTOProvider(注意该类的实现了IDTOProvider接口)。DTOProviderConfiguration类型实际上是抽象类BaseProvider的具体实现,但是加上了[XmlRoot("DTOProvider")]属性(可以反序列化),这样将得到了一个provider类型。
idto = (IDTOProvider)dtoPC.Instance();
dtoPC.Instance();会调用BaseProvider(注意是抽象类)中的
public object Instance()
       {
              return Activator.CreateInstance(System.Type.GetType(this.ProviderType));
}
大家看到,这又是一个动态产生类型的方法,也是采用了反射原理。该类的属性ProviderType声明是这样的
[XmlAttribute("type")]
       public string ProviderType
       {
              get   {      return _type;       }
              set       { _type = value;      }
}
可以看到,这是从配置文件中读取到的Type值,具体到我察看的工程值是:Dottext.Framework.Data.DataDTOProvider, Dottext.Framework
这样会实例化DataDTOProvider类,而DataDTOProvider实现了IDTOProvider接口。通过这样的“复杂”的过程, DTOProvider静态构造了一个可以访问数据库层的接口(DTOProvider.Instance()语句)IDTOProvider,而IDTOProvider中正好可以实现了接口函数
int Create(Entry entry, int[] CategoryIDs);这里相对于SQL数据层的细节如下(见得到的是实现类DataDTOProvider ):
public int Create(Entry entry, int[] CategoryIDs)
         {
              if(entry.PostType == PostType.PingTrack)
              {
                   return DbProvider.Instance().InsertPingTrackEntry(entry);// DbProvider稍后解释
              }
 
              FormatEntry(ref entry);
 
              if(entry is CategoryEntry)
              {
                   entry.EntryID = DbProvider.Instance().InsertCategoryEntry(((CategoryEntry)entry));
              }
              else
              {
                   entry.EntryID = DbProvider.Instance().InsertEntry(entry);
        
                   if(CategoryIDs != null)
                   {
                        DbProvider.Instance().SetEntryCategoryList(entry.EntryID,CategoryIDs);
                   }
              }
 
              if(entry.EntryID > -1)// && Config.Settings.Tracking.UseTrackingServices)
              {
                   entry.Link = Dottext.Framework.Configuration.Config.CurrentBlog().UrlFormats.EntryUrl(entry);
                   Config.CurrentBlog().LastUpdated = entry.DateCreated;
 
              }
              else
              {
                   //we need to fail here to stop the PostCommits?
                   throw new BlogFailedPostException("Your entry could not be added to the datastore");
              }
 
              return entry.EntryID;
         }
在这里,实现了对于Entry实体的实体化存储操作。其中的 DbProvider又是值得关注的
static DbProvider()
         {
              DbProviderConfiguration dpc = Config.Settings.BlogProviders.DbProvider;
              dp = (IDbProvider)dpc.Instance();
              dp.ConnectionString = dpc.ConnectionString;
         }
看到没有,跟DTOProvider又是一样的静态化构造。通过序列化的到具体的DB存储操作实体对象Dottext.Framework.Data.SqlDataProvider, Dottext.Framework。在创建一个Entry对象的DB操作中有如下代码:
entry.EntryID = DbProvider.Instance().InsertEntry(entry);
SqlDataProvider的 InsertEntry操作细节如下:
public int InsertEntry(Entry entry)
         {
              SqlParameter[] p =
              {
                   SqlHelper.MakeInParam("@Title", SqlDbType.NVarChar,255,entry.Title),
                   SqlHelper.MakeInParam("@TitleUrl", SqlDbType.NVarChar,255,DataHelper.CheckNull(entry.TitleUrl)),
                   SqlHelper.MakeInParam("@Text",SqlDbType.NText,0,entry.Body),
                   SqlHelper.MakeInParam("@SourceUrl",SqlDbType.NVarChar,200,DataHelper.CheckNull(entry.SourceUrl)),
                   SqlHelper.MakeInParam("@PostType",SqlDbType.Int,4,entry.PostType),
                   SqlHelper.MakeInParam("@Author",SqlDbType.NVarChar,50,DataHelper.CheckNull(entry.Author)),
                   SqlHelper.MakeInParam("@Email",SqlDbType.NVarChar,50,DataHelper.CheckNull(entry.Email)),
                   SqlHelper.MakeInParam("@Description",SqlDbType.NVarChar,500,DataHelper.CheckNull(entry.Description)),
                   SqlHelper.MakeInParam("@SourceName",SqlDbType.NVarChar,200,DataHelper.CheckNull(entry.SourceName)),
                   SqlHelper.MakeInParam("@DateAdded",SqlDbType.DateTime,8,entry.DateCreated),
                   SqlHelper.MakeInParam("@PostConfig",SqlDbType.Int,4,entry.PostConfig),
                   SqlHelper.MakeInParam("@ParentID",SqlDbType.Int,4,entry.ParentID),
                   SqlHelper.MakeInParam("@EntryName",SqlDbType.NVarChar,150,DataHelper.CheckNull(entry.EntryName)),
                   BlogIDParam,
                   SqlHelper.MakeOutParam("@ID",SqlDbType.Int,4)
                  
              };
              NonQueryInt("blog_InsertEntry",p);
              return (int)p[14].Value;
         }
看到否,这是一个具体的sql存储过程调用代码。
就这样,利用配置文件我们指定了BlogProviders 的DTOProvider 和DbProvider 具体实例,而根据不同的配置,他们是可以替换成不同的实例,譬如可以将DB层换成Mysql.或者Orcal的具体表操作。
以上阅读,大家需要注意:
1、   静态构造函数
2、 Activator.CreateInstance(System.Type.GetType(this.ProviderType)); 这种利用反射创建对象实例的方法。
另外就是需要理解,dottext采用配置文件来动态指定DTO和DB操作的精巧设计(虽然有些让人懵头)。
 
遗憾的是,我发现博客园的版本似乎有问题,很多操作,他们直接操纵了数据库,这样可能会学让我这种不熟悉源版本的人会产生一定的误解。以上分析,希望能够排除大家的疑问,我可是熬了个通宵哦:)
posted @ 2006-07-12 17:35 pack27 阅读(33) | 评论 (0) |  编辑  收藏

[转载] DotText源码阅读(3)-框架配置体系和反序列化
from: http://blog.csdn.net/shanhe/archive/2006/05/05/708639.aspx
dottext框架配置体系 和反序列化
 
配置节是一个比较容易混淆人的专题。Dottext的系统环境配置、单独每一个人的blog配置都是通过自定义的配置节实现的,并且dottext自己实现了其中的处理程序(handler)。也就是说,利用asp.net系统的配置文件作为存储机制,加上了单独处理机制,实现了系统的灵活配置。
在web.config的根元素<configuration>下一开始就声明了自定义配置节处理程序:
             
             
             
             
             
 
其中的最后2项自然不必多讲,属于微软的提供程序和第三方提供程序,在此忽略。我们逐步来看看 Dottext.Framework.Util 下的实现过程,理解其中的逻辑。
用到的自定义处理程序都是 XmlSerializerSectionHandler ,我们看看其中的处理逻辑,蕴含的.net特性:
 
public object Create(object parent, object configContext, System.Xml.XmlNode section)
              {
                     XPathNavigator nav = section.CreateNavigator();
                     string typename = (string) nav.Evaluate("string(@type)");
                     Type t = Type.GetType(typename);
                     XmlSerializer ser = new XmlSerializer(t);
                     return ser.Deserialize(new XmlNodeReader(section));
}
string typename = (string) nav.Evaluate("string(@type)"); 是从当前XML(配置文件是一个符合xml要求的文档)节点处,获取”type”属性,然后按照属性描述,获得一个.net的类型。这里使用到了.net的反射机制。此处的type可以是类 、值类型 、数组 、接口 、指针 、枚举类型。这样,通过配置文件中的xml流(相当于字符串),系统就指定了特定的类。这种生成类的方法是区别于new 方法生成具体类的另外途径,好处就是灵活根据具体环境内容(甚至是用户交互输入的类型描述字符串)就可以生成获得托管类型。(此处反射细节请参考MSDN)。坏处就是可能隐藏着类型错误,运行时出错,导致不可预料(好像这个词在windows编程时代相当的常见)例外甚至系统崩溃。
当然,仅仅创建了类型是不够用的,还需要通过一定途径来确定生成类的具体状态,这有用到OOP语言的重要特性—序列化,将一个对象存储器来,以及从存储中还原具体对象的机制。这里使用的是System提供的 XmlSerializer 类的反序列化方法Deserialize。反序列化后面还有很多代码涉及到,我认为现在就大致理解为“通过这个反序列化,我们刚刚得到的类实例中的属性、成员变量获得了赋值,进入到某个状态,就好像我们此处运行了new 语法和进行了对象的构造以及赋值”即可。更进一步的可以从权威资料MSDN获取。
 
基于以上理解,我们来阅读相关的配置节,并进行解释。
从简单的入手:
              virtualPath ="~/SearchIndex" physicalPath="\SearchIndex" domains="localhost"
              pageSize="20" />
这个配置节,定义了一个 在Dottext.Search程序集中存在的名为“Dottext.Search.SearchConfiguration”的类,在反序列化的时候,我们会对其中的某些属性(urlFormat、virtualPath、physicalPath、domains、pageSize )进行赋值。那么,这些可以反序列化的类有否什么区别于其他“正常类”的地方呢?我们打开这个类看看:
[Serializable]       //注意,这里用到的是属性编程,这个是.net的新特性,通过这个语法声明了名为Serializable的属性给类 SearchConfiguration,告诉.net框架这个类是可以进行序列化和反序列化。默认情况下,所有该类的字段(包括私有)都要序列化和反序列化,但是通过另外指定属性声明,可以灵活处理。
       public class SearchConfiguration
       {
              public static readonly string PermaLink = "permalink";
              。。。。。。//一大堆只读静态字段
              public static readonly string TempIndex = "tempIndex";
             
              public static SearchConfiguration Instance()
              {
                     return (SearchConfiguration)ConfigurationSettings.GetConfig("SearchConfiguration");
//此处就是利用了反射来构造类实例。
              }
 
              public SearchConfiguration()
              {                     //缺盛构造函数
              }
 
              private string _urlFormat = "http://{0}/{1}/{2}/{3}.aspx";
              [XmlAttribute("urlFormat")]       //此处另外声明了UrlFormat属性的序列化和反序列化属性,告诉.net运行时环境此处的字段采用XML节点作为存储进行序列化和反序列化,并且读取的节点名称是“urlFormat”。
              public string UrlFormat
              {
                     get {return this._urlFormat;}
                     set {this._urlFormat = value;}
              }
 
              private string _domains;
              [XmlAttribute("domains")]
              public string Domains
              {
                     get {return this._domains;}
                     set {this._domains = value;}
              }
 
              private int _rebuildInterval = 60;                    
              [XmlAttribute("rebuildInterval")]
              public int RebuildInterval
              {
                     get {return this._rebuildInterval;}
                     set {this._rebuildInterval = value;}
              }
 
              private int _updateInterval = 30;             
              [XmlAttribute("updateInterval")]
              public int UpdateInterval
              {
                     get {return this._updateInterval;}
                     set {this._updateInterval = value;}
              }
 
              private int _pageSize = 50;         
              [XmlAttribute("pageSize")]
              public int PageSize
              {
                     get {return this._pageSize;}
                     set {this._pageSize = value;}
              }
 
              private int _searchResultLimit = 100;       
              [XmlAttribute("searchResultLimit")]
              public int SearchResultLimit
              {
                     get {return this._searchResultLimit;}
                     set {this._searchResultLimit = value;}
              }
 
              private string _virtualPath;             
              [XmlAttribute("virtualPath")]
              public string VirtualPath
              {
                     get {return this._virtualPath;}
                     set {this._virtualPath = value;}
              }
 
              private string _physicalPath;         
              [XmlAttribute("physicalPath")]
              public string PhysicalPath
              {
                     get
                     {
                            if(this._physicalPath == null)
                            {
                                   if(VirtualPath != null)
                                   {
                                          this._physicalPath = HttpContext.Current.Server.MapPath(VirtualPath);
                                   }
                                   else
                                   {
                                          throw new ApplicationException("Physical location of the search index could not be found. Either the physical or virtual location must be specified in your configuration file");
                                   }
                            }
                            return this._physicalPath;
                     }
                     set {this._physicalPath = value;}
              }
       }
 
如果哪一个字段不需要参与序列化和反序列化,应该指定[XmlIgnore]属性标记。
 
接下来,看看后面经常用到的 BlogConfigurationSettings ,该类为“Dottext.Framework.Configuration.BlogConfigurationSettings”,察看该类源代码,我们发现该类也是可序列化和反序列化的。不过注意的是,其中的部分成员属于数组,而数组属于复合数据类型,所以在配置文件声明中是这样的:
                    
                    
                      ……
而在类源代码中是这样的来说明改成员:
private EntryHandler[] _entryHandlers;
[XmlArray("EntryHandlers")]
public EntryHandler[] EntryHandlers
{
       get {return this._entryHandlers;}
       set {this._entryHandlers = value;}
}
通过XmlArray属性,指出了要按照数组方式进行序列化和反序列化,节点的名称是“EntryHandlers”。.net CLR会通过反射机制将配置文件的描述生成EntryHandler[],而其中每一个元素都是Dottext.Framework.EntryHandling.CommentDeliveryHandler,这个过程通过一个短小的[XmlArray("EntryHandlers")]就完成,且又达到了灵活是应需求,展示了.net提供的新特性的威力。HandlerConfiguration也是通过配置获得一个数组,类似机理。
另外,打击需要着重看看
                    
                    
                    
                    
                    
                    
                    
                    
                    
             
此处的配置信息在后面的很多部分都涉及到。看看BlogProviders类(呵呵,当作课外吧),也是一个可序列化和反序列化的类。
整个dottext很多灵活性就是通过这机制体现。
posted @ 2006-07-12 17:26 pack27 阅读(26) | 评论 (0) |  编辑  收藏

[转载] DotText源码阅读(2)-工程、数据库表结构
    内容篇幅较长,请点击 这里阅读全文。
posted @ 2006-07-12 17:24 pack27 阅读(68) | 评论 (0) |  编辑  收藏

[转载] DottText源码阅读(1)-调试
from: http://blog.csdn.net/shanhe/archive/2006/05/04/707457.aspx
如何调试系统
笔者下载的版本是嘟嘟提供的 CNBlogsDottext ,关于这个版的说明在:
http://forums.cnblogs.com/ShowThread.aspx?PostID=485
下载后展开有如下目录存在一个工程文件,是v003版本的工程。因为是asp.net的工程,所以需要配置IIS来确保全部项目夹在成功。具体察看 OtherStuff/安装说明 ,在此列出:
1、建立DottextWeb虚拟目录, 指向CNBlogsDotText\DottextWeb, 该虚拟目录用于VS.NET打开DottextWeb项目, 不要建立*.*到asp.net的映射。
 
2、建立访问站点虚拟目录, 指向CNBlogsDotText\DottextWeb, 并建立*.*到asp.net的映射
 
注释:你看到这里建立了两个虚拟目录。第一个虚拟目录是满足vs2003工程打开需要的,因为工程的配置文件需要采用frontpage方式从IIS读取工程文件,有对此虚拟目录的引用。第二个虚拟目录是为了你运行调试使用的,因为dottext的web.config会接管全部对该目录的http全部类型文件的访问,所以需要IIS把全部请求都传递给asp.net框架,而将*.*映射到asp.net会把保证dottext接管并分析处理每一个对blog应用程序的请求。
具体为了便于测试(保持测试环境和实际运行环境),我自己在本机上创建了一个测试环境,是为了满足同我的公司域名的blog.xxx.cn一致,我另外起用了一个blog.test.com,。我在\system32\drivers\etc\host文件中加入了一行
127.0.0.1        blog.test.com
并且,新建立一个站点主机头为blog.test.com 绑定在我的局域网Ip,并且目录就设定为DottextWeb所在目录,按照2 的要求,添加了*.*到asp.net的映射。这样做,可以在你本地采用blog.text.com调试通过后,可以无需担心什么就直接覆盖上个版本就可以实现更新了。
 
3、安装数据库, 运行OtherStuff\SQL Scripts中有DotTextData.sql脚本,自动创建DotTextData数据库。也可以先建立数据库,然后运行DotTextData.sql脚本。忽略脚本安装错误提示,不影响正常使用。
 
5、初始化数据库,分别运行InitData.sql与SkinControl脚本。
 
6、在web.config中配置数据库连接字符串, 即中的connectionString。
 
注释:这里,需要确保在实际运行和开发机器上的字符串一致,否则改web.config就麻烦死了。同时,需要sql连结客户端配置好(如果采用sql 的话)。
 
7、上述配置完成后,就可以访问了。
 
8、通过“新博客注册”注册一个新用户,并将该用户加入站点管理员组,操作方法:在表blog_UsersInRoles中,添加一条记录,UserID为你刚注册的用户的BlogID,RoleID为1(表blog_Roles中administrators的RoleID)。
 
注释:这个规则的目的是为了你能够有一个管理员账号,实现系统分类的配置。这个账号跟其他申请注册的账号的后台管理界面略有不同,多了一个管理的TAB选项页面。
 
9、添加网站分类:
  A、以管理员用户登录,进入管理页面,打开“Manage”页面
  B、选择“编辑网站分类”,添加一个分类
  C、在分类名称点击“Config”, 再选择添加,就会向SiteBlogConfig.config文件写入数据,如果这时出现“访问被拒绝”的错误,那是因为程序被安装在NTFS分区上,运行IIS进程的用户对程序所在文件夹没有写入权限,你需要修改一下权限设置。
 
10、设置首页默认显示的网站分类,在SiteBlogConfig.config中将相应的设置成true。默认设置“首页”分类为true,会显示所有分类的随笔。
 
注释:经过以上安装,本地的确可以浏览。但是界面相当粗糙,通常,每个公司都会进行适当美化blog站的索引页和其他分栏目的索引页,所以需要先做些美化页面再加入到站点上去。后面会介绍如果修改配置实现替换系统的页面。这些需要在阅读了源码后才心里有底。呵呵。
 
直接在VS中按下调试时,这个时候由于运行的是上文提到的1中的目录,所以此时iis并没有将全部文件扩展映射到asp.net框架,所以会出现很多.net运行时错误,所以你不应该直接调试,而是应该启动进程调试。具体方法为:
先采用blog.test.com启动首页(让系统启动一个w3wp.exe,2000版本的是),然后在 调试菜单中,选定调试进程,在出现的进程列表中,找到blog.test.com 对应的应用程序进程,然后附加上,并确保CLR调试选项选定。此时,VS会进入调试状态,我们再启动访问相应的页面,如果加入了断点,会相应进入断点跟踪状态。
 
 Update:
看到有兄弟留言说还搞不定调试,再次补充:
0、确保你是调试机器的管理员
1、首先确保你的工程是debug版本,默认下载的好象是release版本。这个不用我说怎么做吧
2、配置好blog.test.com的本地解析,确保cmd.exe状态下ping blog.test.com 是本机IP
3、配置好主机头,在第二个blog站点加上这个主机头
4、访问 blog.test.com 这个时候系统会启动一个asp.net的应用程序,2000操作系统上是aspnet_wp.exe,2003系统是w3wp.exe,这个在进程列表中可以看到的进程就是你需要调试的
5、点调试菜单,找到“进程...”选择后,出现了一个仅测和列表框,你会看到有aspnet_wp.exe或者w3wp.exe在其中,选择刚启动的那个。至于如果有好几个同名进程,那。。。简单点 就是将w3svc服务重新启动,然后其他的什么都不做,访问blog.test.com一次,自然就只有一个asp.net应用程序进程。
6、选中它,点击“附加...”会自然附加,弹出的对话框中的common language runtime必须被选择
7、确定关闭后,vs会进入调试状态,你可以随意加入断点,然后通过访问特定的url来激活断点。
8、如果还搞不定,请先请您公司的同事,然后再考虑发贴问人。我的email:myjobsdk#yahoo.com.cn,我只能不定期检查这个邮件来做本文的力所能及的支持。
谢谢关注,希望大家能够补充我的不足!
 
2006-5-11 update:
有兄弟询问如何配置*.*到asp.net的映射,特此在此补充:
windows2000 和windows2003做的配置是不一样的,
2000的配置如下:
     1、iis管理器,打开blog站点的属性
     2、主目录,在 应用程序设置中 选择 配置。。。
     3、应用程序映射,点击 添加。。。
     4、可执行文件选择  WINNT\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll 这个需要同你的.net framework当前版本一致
     5、扩展名 输入 *, 方法:GET,POST
     6、不要选中 “检查文件是否存在”,选定限于脚本引擎
2003 无法输入*到aspnet_isapi.dll 的映射,需要:
      1、iis管理器,打开blog站点的属性
     2、主目录,在 应用程序设置中 选择 配置。。。
     3、映射,应用程序扩展是没法输入*到 aspnet_isapi.dll 的映射  
     4、在下面的 通配符应用程序映射 (执行顺序)中 插入 C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\aspnet_isapi.dll,也要注意 不要选择“确认文件是否存在”
不要选择“确认文件是否存在”的目的是避免访问一些物理文件不存在的资源时候可以被aspnet_isapi.dll处理请求,也就是进入.net执行框架
posted @ 2006-07-12 17:18 pack27 阅读(44) | 评论 (0) |  编辑  收藏

[转载] DotText源码阅读(0)

from: http://blog.csdn.net/shanhe/archive/2006/05/02/705039.aspx

a)         源代码阅读说明
                         i.              本源代码源自博客园的CNBlogsDottext,其中有些属于博客园的高手们进行了增补,当然主要是汉化工作。博客园是国内较早能够阅读分析dottext,并成功修改实施的技术组织,做了大量的工作。所以本人在此基础上进行修改,并进行阅读,先感谢他们。
                       ii.              不过其中有些代码和数据结构修改,没有详细说明,我没有大量精力阅读 Scott Watermasysk 的版本,所以其中可能有些误解,如果错误,算我的,特此说明。
                      iii.              本人也是工作需要,粗粗在博客园的基础上进行了修改后使用,后来其他同事要跟进维护,发现缺少相当多的资料和文档,找到我寻求帮助,为了长久计特在休息时间进行整理,方便后续维护。既然做了,可能其他同学也会需要,就决定在blog上发表,供参考。错了,大家指正,没说到的、不深入的,大家补充。
b)        如何阅读代码
                         i.              首先,我们要确保工程安装正确,且能够在VS中进行调试,具体调试手段和技巧主要来自于博客园发表的相关文档,建议大家先熟悉博客园上的有关专题资料
                       ii.              阅读的目的是两个:1、为了维护目的做一个文档记录。2、为了学习其中用到的一些技术和技巧,进一步认识.net的开发核新特性。
                      iii.              我们需要进行一些测试目的的修改,以验证自己的理解。确保文档内容对于维护有参考价值
                     iv.              不会抄大段代码,所以需要阅读时候参照工程中的代码,要确保代码版本和来源的一致性。
c)        分那些主要专题
                         i.              如何安装、调试
                       ii.              数据结构文档补充
                      iii.              dottext的体系说明
                     iv.              数据访问
                       v.              配置和序列化(重点)
                     vi.              皮肤和模版
                    vii.              pingback/trackBack
                  viii.              搜索
                     ix.              ORM
                       x.              发布后台的一些主题:发表文章、收藏、相册、留言、管理配置
                     xi.              设计模式分析
                    xii.              其他问题
 
d)        可能并不按照以上规划次序来写,也有可能合并其中的各部分,也可能增加专题,更希望其他高手参与。
 
e)         以次序言开始,督促自己要写下去这个专题,虽然自己的时间很紧张。
相关连结:
DotText源码阅读(1)-调试
DotText源码阅读(2)-工程、数据库表结构
DotText源码阅读(3)-框架配置体系和反序列化
dotText源码阅读(4)--DTO和数据访问
dotText源码阅读(5)--URLreWrite和Handler
DotText源码阅读(6) --模版皮肤
posted @ 2006-07-12 17:16 pack27 阅读(28) | 评论 (0) |  编辑  收藏

推荐系列文章:《DotText源码阅读》

DotText源码阅读 作者:shanhe

DotText源码阅读(0) DotText源码阅读(1)-调试 DotText源码阅读(2)-工程、数据库表结构 DotText源码阅读(3)-框架配置体系和反序列化 dotText源码阅读(4)--DTO和数据访问 dotText源码阅读(5)--URLreWrite和Handler DotText源码阅读(6) --模版皮肤 DotText源码阅读(7) --Pingback-TrackBack

 

posted @ 2006-07-12 16:00 pack27 阅读(424) | 评论 (0) |  编辑  收藏

2006年5月26日 #

不费吹灰之力,给博客首页添加“最近评论”栏

1、将UI\Controls\RecentComments.ascx和.cs复制到AggSute\;

2、在AggSite\Template.ascx相关位置添加下面两行:
<%@ Register TagPrefix="uc1" TagName="RecentComments" Src="RecentComments.ascx" %>

3、把AggSite\RecentComments.ascx中的
namespace Dottext.Web.UI.Controls
改为:
namespace Dottext.Web.AggSite

Inherits="Dottext.Web.UI.RecentComment"
改为:
Inherits="Dottext.Web.AggSite.RecentComments"

4、把AggSite\RecentComments.ascx.cs中

BindList()函数
query.ItemCount=Config.CurrentBlog().ItemCount;
改为:
query.ItemCount=5; //显示评论的数目

OnLoad(EventArgs e)函数注释掉下面语句:
this.Visible=Dottext.Web.UI.Globals.CheckContorVisible("RecentComments");
string url=Config.CurrentBlog().FullyQualifiedUrl;
RSSHyperlink1.NavigateUrl=url+"RecentCommentsRSS.aspx";

5、修改样式,然后重新编译DotText。OK!

你可能感兴趣的:(DotText源码阅读)