zoukankan      html  css  js  c++  java
  • DotText源码阅读

    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<hanlers.Length; 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<links.Count; 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 = @"<rdf:\w+\s[^>]*?>(</rdf:rdf>)?";匹配的部分,分析出其中的引用通告地址。下一步就是利用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)编辑 收藏

    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很简单,仅仅包含了数个嵌套的容器控件
    <body>
              <form id="Form1" method="post" runat="server">
                  <DT:MASTERPAGE id="MPContainer" runat="server">
                       <DT:contentregion id="MPMain" runat="server">
                           <asp:PlaceHolder id="CenterBodyControl" runat="server"></asp:PlaceHolder>
                       </DT:contentregion>
                  </DT:MASTERPAGE></form>
    </body>
    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)编辑 收藏

    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<count; 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)编辑 收藏

    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<count;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")]属性,从<BlogProviders>节获得的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)编辑 收藏

    from: http://blog.csdn.net/shanhe/archive/2006/05/05/708639.aspx
    dottext框架配置体系 和反序列化
     
    配置节是一个比较容易混淆人的专题。Dottext的系统环境配置、单独每一个人的blog配置都是通过自定义的配置节实现的,并且dottext自己实现了其中的处理程序(handler)。也就是说,利用asp.net系统的配置文件作为存储机制,加上了单独处理机制,实现了系统的灵活配置。
    在web.config的根元素<configuration>下一开始就声明了自定义配置节处理程序:
    <configSections>
                  <section name="BlogConfigurationSettings" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
                  <section name="HandlerConfiguration" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
                  <section name="SearchConfiguration" type="Dottext.Framework.Util.XmlSerializerSectionHandler, Dottext.Framework" />
                  <section name="microsoft.web.services" type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, Microsoft.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
                  <section name="codeHighlighter" type="ActiproSoftware.CodeHighlighter.CodeHighlighterConfigurationSectionHandler, ActiproSoftware.CodeHighlighter" />
    </configSections>
     
    其中的最后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获取。
     
    基于以上理解,我们来阅读相关的配置节,并进行解释。
    从简单的入手:
    <SearchConfiguration type="Dottext.Search.SearchConfiguration, Dottext.Search" urlFormat="http://{0}/{1}/{2}/{3}.aspx"
                  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”,察看该类源代码,我们发现该类也是可序列化和反序列化的。不过注意的是,其中的部分成员属于数组,而数组属于复合数据类型,所以在配置文件声明中是这样的:
    <EntryHandlers>
                         <EntryHandler type="Dottext.Framework.EntryHandling.CommentFormatHandler, Dottext.Framework" postType="Comment"       processAction="Insert" processState="PreCommit" isAsync="false" />
                         <EntryHandler type="Dottext.Framework.EntryHandling.CommentDeliveryHandler, Dottext.Framework"       postType="Comment" processAction="Insert" processState="PostCommit" isAsync="true" />
                          ……
    </EntryHandlers>
    而在类源代码中是这样的来说明改成员:
    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>
                         <!-- Controls how .Text formats Urls -->
                         <UrlFormatProvider type="Dottext.Framework.Format.UrlFormats, Dottext.Framework" />
                         <DTOProvider type="Dottext.Framework.Data.DataDTOProvider, Dottext.Framework" />
                         <!--
                                       By default .Text uses SQL Server as the backend data store. The DbProvider determines which DbProvider
                                       (a class which implements IDbProvider) is used. This is optional.
                         -->
                         <DbProvider type="Dottext.Framework.Data.SqlDataProvider, Dottext.Framework" connectionString="user id=ad;password=cbiqadjsd;initial Catalog=Zixun_dataBase;Data Source=211" />
                         <ConfigProvider type="Dottext.Common.Config.MultipleBlogConfig, Dottext.Common" host="localhost"       cacheTime="120" />
                         <!--
                         <ConfigProvider type = "AspNetWeb.MSBlogsConfigProvider, MsftBlogsHttpModule"
                                cacheTime = "120"/>
                         -->
                         <!-- Controls how .Text sends email. By default, SystemMail is used. -->
                         <EmailProvider type="Dottext.Framework.Email.SystemMail, Dottext.Framework" smtpServer="localhost"       adminEmail="EMAIL" />
                  </BlogProviders>
    此处的配置信息在后面的很多部分都涉及到。看看BlogProviders类(呵呵,当作课外吧),也是一个可序列化和反序列化的类。
    整个dottext很多灵活性就是通过这机制体现。
    posted @ 2006-07-12 17:26 pack27 阅读(26) | 评论 (0)编辑 收藏

        内容篇幅较长,请点击这里阅读全文。
    posted @ 2006-07-12 17:24 pack27 阅读(68) | 评论 (0)编辑 收藏

    from: http://blog.csdn.net/shanhe/archive/2006/05/04/707457.aspx
    如何调试系统
    笔者下载的版本是嘟嘟提供的CNBlogsDottext ,关于这个版的说明在:
    下载后展开有如下目录存在一个工程文件,是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中配置数据库连接字符串, 即<DbProvider>中的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中将相应的<SiteBlogConfig>设置成<IsDefault>true</IsDefault>。默认设置“首页”分类为<IsDefault>true</IsDefault>,会显示所有分类的随笔。
     
    注释:经过以上安装,本地的确可以浏览。但是界面相当粗糙,通常,每个公司都会进行适当美化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)编辑 收藏

    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源码阅读(5)--URLreWrite和Handler
    DotText源码阅读(6) --模版皮肤
    posted @ 2006-07-12 17:16 pack27 阅读(28) | 评论 (0)编辑 收藏

    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" %>
    <uc1:RecentComments id="RecentComments" runat="server"></uc1:RecentComments>

    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!

  • 相关阅读:
    Asp.net MVC 传递数据 从前台到后台,包括单个对象,多个对象,集合 前端javascript数组,后端接受
    jquery each循环遍历完再执行的方法 因为each是异步的 所以要加计数器.
    分布式中的一些概念,分布式简单理解,分布式架构的常见概念
    href="#"与href="javascript:void(0)"的区别
    Newtonsoft.Json高级用法DataContractJsonSerializer,JavaScriptSerializer 和 Json.NET即Newtonsoft.Json datatable,dataset,modle,序列化
    Newtonsoft.Json高级用法,json序列号,model反序列化,支持序列化和反序列化DataTable,DataSet,Entity Framework和Entity,字符串
    C#利用反射实现两个类的对象之间相同属性的值的复制,一个对象的属性复制给另一个对性的属性
    指定文件兼容性模式 < meta http-equiv = "X-UA-Compatible" content = "IE=edge,chrome=1" />的意义
    使用 IIS 在 Windows 上托管 ASP.NET Core2.0
    jQuery.data() 的实现方式,jQuery16018518865841457738的由来,jQuery后边一串数字的由来
  • 原文地址:https://www.cnblogs.com/cxd4321/p/573629.html
Copyright © 2011-2022 走看看