65.9K
CodeProject 正在变化。 阅读更多。
Home

内部网站搜索引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (71投票s)

2004年1月29日

16分钟阅读

viewsIcon

901441

downloadIcon

31324

站点搜索引擎可以搜索整个页面以及动态页面中的匹配关键字或短语,并计算关键字或短语在页面中出现的次数,然后将匹配度最高的搜索结果排在前面显示。

Sample Image

引言

搜索引擎模块将搜索整个页面以及动态页面中匹配的关键字或短语,并计算关键字或短语在页面中出现的次数,然后将匹配度最高的搜索结果排在前面显示。该模块将搜索所有你可以在 web.config 文件中指定文件扩展名的文件。你不希望被搜索的文件或文件夹也可以在 web.config 文件中指定,以便不对这些文件和文件夹进行搜索。现在你还可以选择你喜欢的编码。

本更新的文章包含本地化和增强代码的技巧

注意:它最适合小型站点。你也可以通过使用正则表达式修改此代码以在内部爬行页面。对于大型站点,你需要定期将数据写入 XML 文件,然后再从 XML 文件读取。我在该部分的末尾提供了一些技巧。我还包括了一个读取和写入 XML 的演示项目。

背景

站点搜索引擎可帮助用户追踪他感兴趣的页面。在我从事 ASP.NET 项目时,我需要添加站点搜索模块。我有一个 ASP 版的,但没有 .NET 版的。因此,这个站点搜索引擎应运而生。我的第一个版本只是一个单独的 Web 窗体,我没有充分利用面向对象 .NET 语言的全部功能。在业余时间,我重写了我的代码,以最大限度地利用面向对象语言。对于这篇文章,我根据经验和不同作者建议的最佳实践,进一步增强了我的设计。

来自中国北京的宋涛先生向我咨询了如何将该模块转换为中文。在他的帮助下,我增强了代码以支持其他语言。此外,一些用户在将 SiteSearch.aspx 放在根目录下时遇到了文章错误。我修改了代码以纠正此错误。

源代码概述

站点搜索引擎的结构如下

定义类和创建类实例的能力是任何面向对象语言最重要的功能之一。在下一节中,我们将看到我们在搜索模块中使用的类。

类名 描述
SiteSearch Web 窗体的类,用户可以在其中搜索站点中的特定词语。
Searches.CleanHtml 清理 HTMl 内容的类
Searches.FileContent 从 HTML 文件中获取内容的类
Searches.Page 存储页面数据的类
Searches.PagesDataset 创建和存储数据集结果的类
Searches.Site 读取站点配置的类
Searches.UserSearch 存储每个用户搜索信息的类

SiteSearch.aspx

Web 窗体是 Microsoft .NET 计划中令人兴奋的新功能之一。SiteSearch.aspx 是一个 Web 窗体,也是搜索模块的起始页。

Web 窗体页面由页面(ASPX 文件)和代码隐藏文件(.aspx.cs 文件或 .aspx.vb 文件)组成。我们的 Web 窗体包含 SiteSearch.aspxSiteSearch.aspx.vb。我将同时处理它们,并重点介绍 Web 窗体的主要元素。

ASP.NET 是一个事件驱动的编程环境。我们将在下一节中看到一些事件处理程序和方法。

Page_Load

服务器控件加载到 Page 对象上,此时视图状态信息可用。Page_Load 事件检查 sSite 是否为 null,并将 Session("Site") 变量分配给它。

  Private Sub Page_Load(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles MyBase.Load
    If IsNothing(sSite) Then
      sSite = Session("Site")
    End If
  End Sub

srchbtn_Click

搜索按钮事件在点击搜索按钮时触发。在这里,我们编写代码来更改控件设置或在页面上显示文本。在这里,我们检查搜索是否包含文本,然后调用 SearchSite 方法。调用 DisplayContent() 为 Web 页面中的不同控件赋值。

  '*********************************************************

  '

  ' srchbtn_Click event

  '

  ' Add code to this event.

  '

  '**********************************************************

  Private Sub srchbtn_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles srchbtn.Click
    Dim strSearchWords As String
    'If there is no words entered by the user to search for 

    'then don't carryout the file search routine

    pnlSearchResults.Visible = False
    strSearchWords = Trim(Request.Params("search"))

    If Not strSearchWords.Equals("") Then
      Searchs.Site.ApplicationPath = String.Format("http://{0}{1}", 
       Request.ServerVariables("HTTP_HOST"), Request.ApplicationPath)
      sSite = SearchSite(strSearchWords)
      Session("Site") = sSite
      dgrdPages.CurrentPageIndex = 0
      DisplayContent()

    End If
  End Sub

DisplayContent

调用 DisplayContent() 为 Web 页面中的不同控件赋值。DataGrid 的内容通过调用 BindDataGrid 方法设置。ViewState("SortExpression") 用于存储排序表达式。

  '*********************************************************************

  '

  ' DisplayContent method

  '

  ' The data is bound to the respective fields.

  '

  '*********************************************************************

  Private Sub DisplayContent()
    If Not IsNothing(sSite.PageDataset) Then
      pnlSearchResults.Visible = True
      lblSearchWords.Text = sSite.SearchWords

      If ViewState("SortExpression") Is Nothing Then
        ViewState("SortExpression") = "MatchCount Desc"
      End If

      BindDataGrid(ViewState("SortExpression"))
      lblTotalFiles.Text = sSite.TotalFilesSearched
      lblFilesFound.Text = sSite.TotalFilesFound
    End If
  End Sub

搜索

搜索的主要调用发生在此方法中。稍后将介绍的 UserSearch 类存储了整个搜索信息和搜索结果。创建 UserSearch 对象,即 srchSite,并为其 SearchWordsSearchCriteria 等属性赋值。同时调用 srchSite.Search 方法。

   '************************************************************

   '

   ' SearchSite method

   '

   ' The sSite.PageDataset is used to populate the datagrid.

   '

   '************************************************************

   Private Function SearchSite(ByVal strSearch_
          As String) As Searchs.UserSearch
    Dim srchSite As Searchs.UserSearch
    srchSite = New Searchs.UserSearch()
    'Read in all the search words into one variable

    srchSite.SearchWords = strSearch

    If Phrase.Checked Then
      srchSite.SearchCriteria = Searchs.SearchCriteria.Phrase
    ElseIf AllWords.Checked Then
      srchSite.SearchCriteria = Searchs.SearchCriteria.AllWords
    ElseIf AnyWords.Checked Then
      srchSite.SearchCriteria = Searchs.SearchCriteria.AnyWords
    End If

    srchSite.Search(Server.MapPath("./"))
    Return srchSite
  End Function

DataGrid

DataGrid 控件呈现一个多列、完全模板化的网格,它是所有数据绑定控件中最通用的。此外,DataGrid 控件是 ASP.NET 中进行数据报告的首选控件。因此,我使用它来显示搜索结果。由于本文的重点是内部搜索引擎,我将只简要概述此处使用的 DataGrid 的功能。

数据绑定

数据绑定是从数据源检索数据并将其动态关联到视觉元素属性的过程。由于 DataGrid 同时处理(或至少在内存中保留)更多项,因此您应该将 DataGrid 显式关联到数据集合,即数据源。

DataGrid 的内容通过其 DataSource 属性设置。所有搜索结果都存储在 sSite.PageDataset.Tables("Pages") 中。因此,DataGrid 的内容被设置为 dvwPages,即 sSite.PageDataset.Tables("Pages").DefaultView。每次页面加载时都会调用 BindDataGrid 方法。

   '************************************************************

   '

   ' BindDataGrid method

   '

   ' The sSite.PageDataset is used to populate the datagrid.

   '

   '************************************************************

   Private Sub BindDataGrid(ByVal strSortField As String)
      Dim dvwPages As DataView
      dvwPages = sSite.PageDataset.Tables("Pages").DefaultView
      dvwPages.Sort = strSortField
      dgrdPages.DataSource = dvwPages
      dgrdPages.DataBind()
   End Sub

该控件能够自动生成基于数据源结构的列。自动生成是 DataGrid 的默认行为,但您可以通过一个名为 AutoGenerateColumnsBoolean 属性来控制此行为。当您希望控件仅显示添加到 Columns 集合中的列时,将该属性设置为 False。当您希望控件添加数据源所需的尽可能多的列时,将其设置为 True(默认)。自动生成不允许您指定标题文本,也不提供文本格式。因此,我在这里将其设置为 False。您通常使用 <columns> 标签在 <asp:datagrid> 服务器控件的主体内绑定列。

 <Columns>
 <asp:TemplateColumn>
  <ItemTemplate>
   <%# DisplayTitle(Container.DataItem( "Title" ), _
        Container.DataItem( "Path" )) %>
   <br>
   <%# Container.DataItem( "Description" ) %>
   <br>
   <span class="Path">
    <%# String.Format("{0} - {1}kb", DisplayPath( _
         Container.DataItem( "Path" )) , _
         Container.DataItem( "Size" ))%>
   </span>
   <br>
   <br>
  </ItemTemplate>
 </asp:TemplateColumn>
</Columns>

使用 DisplayTitle 方法和 DisplayPath 方法来显示 DataGrid 列中自定义的信息。

   '****************************************

   '

   ' DisplayTitle method

   '

   ' Display title of searched pages 

   '

   '****************************************

   Protected Function DisplayTitle(ByVal Title _ 
       As String, ByVal Path As String) As String
      Return String.Format("<A href="{1}">{0}</A>", Title, Path)
   End Function

   '****************************************

   '

   ' DisplayPath method

   '

   ' Path of the file is returned 

   '

   '****************************************

   Protected Function DisplayPath(ByVal Path As String) As String
      Return String.Format("{0}{1}/{2}", _ 
       Request.ServerVariables("HTTP_HOST"), _ 
       Request.ApplicationPath, Path)
   End Function
分页

DataList 控件不同,DataGrid 控件支持数据分页,即能够将显示的数据源行分成页面。我们数据源的大小很容易超出页面空间。因此,为了保持服务器的可伸缩性并为用户提供更易于访问的页面,一次只显示几行。要启用 DataGrid 控件的分页,您需要告知控件。您可以通过 AllowPaging 属性来完成此操作。

分页栏是 DataGrid 控件提供的一个有趣且互补的功能,让用户可以轻松地从一页移动到另一页。分页栏是显示在 DataGrid 控件底部的一行,其中包含指向可用页面的链接。当您单击其中任何一个链接时,控件会自动触发 PageIndexChanged 事件并相应地更新页面索引。当页面索引更改时,将调用 dgrdPages_PageIndexChanged

   '*****************************************************************

   '

   ' dgrdPages_PageIndexChanged event

   '

   ' The CurrentPageIndex is Assigned the page index value.

   ' The datagrid is then populated using the BindDataGrid function.

   '

   '*****************************************************************

   Protected Sub dgrdPages_PageIndexChanged(ByVal s As Object, _ 
     ByVal e As DataGridPageChangedEventArgs) _ 
     Handles dgrdPages.PageIndexChanged
   dgrdPages.CurrentPageIndex = e.NewPageIndex
   DisplayContent()
   End Sub

您可以使用 PagerStyle 属性的 Mode 属性来控制分页栏。Mode 属性的值来自 PagerMode 枚举。在这里,我们选择了一系列详细的数字按钮,每个按钮指向一个特定的页面。

<PagerStyle CssClass="GridPager" Mode="NumericPages"></PagerStyle>
排序

DataGrid 控件实际上不排序行,但只要底层数据源的排序功能足够,它就提供了良好的排序支持。数据源始终负责根据用户通过 DataGrid 控件的用户界面选择的排序表达式返回排序后的记录集。通过将 AllowSorting 属性设置为 True 来触发内置排序机制。

调用 dgrdPages_SortCommand 来对 DataGrid 进行排序。SortCommand 事件处理程序通过 DataGridSortCommandEventArgs 类提供的 SortExpression 属性了解排序表达式。在我们的代码中,排序信息之所以能够持久化,是因为它存储在页面 ViewState 集合的一个槽中。

注意:在我的页面中,我禁用了标题,但如果显示了标题,您可以使用它来对 DataGrid 进行排序。

   '*****************************************************************

   '

   ' dgrdAdditionalItems_SortCommand event

   '

   ' The ViewState( "SortExpression" ) is Assigned

   ' the sort expression value.

   ' The datagrid is then populated using the BindDataGrid function.

   '

   '*****************************************************************

   Protected Sub dgrdPages_SortCommand(ByVal s As Object, _ 
       ByVal e As DataGridSortCommandEventArgs) _ 
       Handles dgrdPages.SortCommand
      ViewState("SortExpression") = e.SortExpression
      DisplayContent()
   End Sub

Page.vb

Page 对象的作用是存储与网站每个页面相关的数据。

Page 类定义了以下属性

Path 存储文件的路径
标题 存储 HTML 标题标签中的文本
关键词 存储 HTML meta keywords 标签中的文本
描述 存储 HTML meta description 标签中的文本
目录 存储 HTML 页面中的文本
Matchcount 存储在 HTML 页面中找到的匹配项
    '***************************************************

    '

    ' Size Property

    '

    ' Assign and retrieve size of the file

    '

    '***********************************************


    Public Property Size() As Decimal
      Get
        Return m_size
      End Get
      Set(ByVal Value As Decimal)
        m_size = Value
      End Set
    End Property
        
    '***************************************************

    '

    ' Path Property

    '

    ' Assign and retrieve path of the file

    '

    '***********************************************


    Public Property Path() As String
      Get
        Return m_path
      End Get
      Set(ByVal Value As String)
        m_path = Value
      End Set
    End Property

    '***********************************************

    '

    ' Title Property

    '

    'Assign and retrieve title of the file

    '

    '***********************************************

    Public Property Title() As String
      Get
        Return m_title
      End Get
      Set(ByVal Value As String)
        m_title = Value
      End Set
    End Property

    '***********************************************

    '

    ' Keywords Property

    '

    ' Assign and retrieve Keywords

    ' (meta tags) of the file

    '

    '***********************************************

    Public Property Keywords() As String
      Get
        Return m_keywords
      End Get
      Set(ByVal Value As String)
        m_keywords = Value
      End Set
    End Property


    '***********************************************

    '

    ' Description Property

    '

    ' Assign and retrieve description

    ' (meta tags) of the file

    '

    '***********************************************

    Public Property Description() As String
      Get
        Return m_description
      End Get
      Set(ByVal Value As String)
        m_description = Value
      End Set
    End Property

    '***********************************************

    '

    ' Contents Property

    '

    ' Assign and retrieve contents of the file

    '

    '***********************************************

    Public Property Contents() As String
      Get
        Return m_contents
      End Get
      Set(ByVal Value As String)
        m_contents = Value
      End Set
    End Property


    '***********************************************

    '

    ' Contents Property

    '

    ' Assign and retrieve MatchCount of the file

    '

    '***********************************************

    Public Property MatchCount() As Integer
      Get
        Return m_matchcount
      End Get
      Set(ByVal Value As Integer)
        m_matchcount = Value
      End Set
    End Property

Page 类有两个私有方法和两个公共方法。它定义了以下方法

CheckFileInfo 方法

这是一个公共方法,用于检查标题、描述和内容是否存在。如果标题文本为空,则将其设置为默认值“无标题”。如果描述文本为空,则将其设置为文件内容或默认值“此页面没有可用描述”。

   '*************************************************

   '

   ' CheckFileInfo method

   '

   ' Subroutine to the check the file contains

   ' title and decription 

   '

   '*************************************************

   Public Sub CheckFileInfo()

     'If the page contains no title then Page Title

     ' variable the appropriate message to display

     If IsNothing(m_title) Or m_title.Trim().Equals("") Then
      m_title = "No Title"
     End If


     'If the page contains no title then Page Description

     'variable the appropriate message to display

     If IsNothing(m_description) Or _
       m_description.Trim().Equals("") Then
      If IsNothing(m_contents) Or _
       m_contents.Trim().Equals("") Then
        m_description = _
         "There is no description available for this page"
      Else
        If m_contents.Length > 200 Then
         m_description = m_contents.Substring(0, 200)
        Else
         m_description = m_contents
        End If
      End If
     End If
   End Sub

Search 方法

Search 方法是一个公共方法,它根据搜索条件调用 SearchPhraseSearchWords 方法。SearchPhrase 方法搜索短语,而 SearchWords 搜索所有或任意单词。这两种方法都调用 SearchPattern 方法,该方法使用正则表达式搜索文件。

      
    '*******************************************

    '

    ' Search method

    '

    ' Subroutine to the search file 

    '

    '*******************************************

    Public Sub Search(ByVal strSearchWords As String, _ 
          ByVal SrchCriteria As SearchCriteria)

      'If the user has choosen to search by phrase 

      If SrchCriteria = SearchCriteria.Phrase Then
        SearchPhrase(strSearchWords)
        'Else the search is either by all or any words

      Else
        SearchWords(strSearchWords, SrchCriteria)
      End If

    End Sub

    '******************************************************

    '

    ' SearchPhrase method

    '

    ' Subroutine to the search file 

    '

    '******************************************************

    Private Sub SearchPhrase(ByVal strSearchWords As String)

      Dim mtches As MatchCollection

      mtches = SearchPattern(strSearchWords)

      'Check to see if the phrase has been found

      If mtches.Count > 0 Then
        'Get the number of times the phrase is matched

        m_matchcount = mtches.Count
      End If
    End Sub

    '**************************************************

    '

    ' SearchWords method

    '

    ' Subroutine to the search file 

    '

    '**************************************************

    Private Sub SearchWords(ByVal strSearchWords As String, _
       ByVal SrchCriteria As SearchCriteria)
      Dim intSearchLoopCounter As Integer
      Dim sarySearchWord As String() 
        'Array to hold the words to be searched for

      Dim mtches As MatchCollection

      'Split each word to be searched up and place in an array

      sarySearchWord = Split(Trim(strSearchWords), " ")

      'Loop round to search for each word to be searched

      For intSearchLoopCounter = 0 To UBound(sarySearchWord)

        'Set the pattern to search for

        mtches = SearchPattern(sarySearchWord(_
        intSearchLoopCounter))

        If SrchCriteria = SearchCriteria.AnyWords Then
          m_matchcount = m_matchcount + mtches.Count
        ElseIf SrchCriteria = SearchCriteria.AllWords Then
          'Check to see if any of the words have been found

          If mtches.Count > 0 Then
            'Get the number of times the search word is matched


            If m_matchcount = 0 Or (m_matchcount > 0 _
                And m_matchcount > mtches.Count) Then
              m_matchcount = mtches.Count
            End If
          Else
            'If the search word is not found then set the 

            'search found variable back to false as one of 

            'the words has not been found

            m_matchcount = 0
            Exit Sub

          End If
        End If
      Next
    End Sub

转义字符 \b 是一个特例。在正则表达式中,\b 表示单词边界(\w 和 \W 字符之间),除非在 [] 字符类中,此时 \b 指的是退格字符。在替换模式中,\b 始终表示退格。
当我们使用 UTF-8 以外的编码时,可能需要删除单词边界。

    '****************************************************

    '

    ' SearchPattern method

    '

    ' Subroutine to the search file 

    '

    '****************************************************

    Private Function SearchPattern( _
       ByVal strSearchWord As String) As MatchCollection
      Dim regexp As Regex
      Dim strPattern

      'Set the pattern to search for

      regexp = New Regex("", RegexOptions.IgnoreCase)

      'Search the file for the phrase

      If Searchs.Site.Encoding.Equals("utf-8") Then
        strPattern = "\b{0}\b"
      Else
        strPattern = "{0}"
      End If

      Return regexp.Matches(m_contents, String.Format(strPattern, _
       strSearchWord), RegexOptions.IgnoreCase)

    End Function

UserSearch.vb

它包含以下属性

SearchCriteria 用户选择的搜索在此存储和检索
SearchWords 用户使用的搜索词在此存储和检索
TotalFilesSearched 此处读取搜索的总文件数
TotalFilesFound 此处读取搜索到的总文件数
    '**********************************************************

    '

    ' SearchCriteria Property

    '

    ' Assign and retrieve SearchCriteria of the site

    '

    '**********************************************************

    Public Property SearchCriteria() As Searchs.SearchCriteria
      Get
        Return m_searchCriteria
      End Get
      Set(ByVal Value As Searchs.SearchCriteria)
        m_searchCriteria = Value
      End Set
    End Property

    '**********************************************************

    '

    ' SearchWords Property

    '

    'Assign and retrieve SearchWords of the site

    '

    '**********************************************************

    Public Property SearchWords() As String
      Get
        Return m_searchWords
      End Get
      Set(ByVal Value As String)
        m_searchWords = Value
      End Set
    End Property

    '**********************************************************

    '

    ' TotalFilesSearched Property

    '

    ' Retrieve TotalFilesSearched of the site

    '

    '**********************************************************

    Public ReadOnly Property TotalFilesSearched() As Integer
      Get
        Return m_totalFilesSearched
      End Get
    End Property

    '**********************************************************

    '

    ' TotalFilesFound Property

    '

    ' Retrieve TotalFilesFound of the site

    '

    '**********************************************************

    Public ReadOnly Property TotalFilesFound() As Integer
      Get
        Return m_totalFilesFound
      End Get
    End Property

    '**********************************************************

    '

    ' PageDataset Shared Property

    '

    ' Retrieve data of the entire site of the site

    '

    '**********************************************************

    Public ReadOnly Property PageDataset() As DataSet
      Get
        Return m_dstPages
      End Get
    End Property

Search 方法

搜索的实际处理从这里开始。此处创建用于存储搜索结果的 DataSet,并调用 ProcessDirectory 方法。

    '********************************************

    '

    ' Search Method

    '

    ' Search the entire site

    '

    '********************************************

    Public Function Search(ByVal targetDirectory As String) As DataSet
      'If the site is in English then use the server HTML encode method

      If Searchs.Site.EnglishLanguage = True Then
        'Replace any HTML tags with the HTML codes 

        'for the same characters (stops people entering HTML tags)

        m_searchWords = m_page.Server.HtmlEncode(m_searchWords)
        'If the site is not english just change the script tags

      Else
        'Just replace the script tag <> with HTML encoded < and >

        m_searchWords = Replace(m_searchWords, "<", "<", 1, -1, 1)
        m_searchWords = Replace(m_searchWords, ">", ">", 1, -1, 1)
      End If
      If m_dstPages Is Nothing Then
        m_dstPages = Searchs.PagesDataset.Create()
      End If
      ProcessDirectory(targetDirectory)
      Return m_dstPages
    End Function

ProcessDirectory 方法

ProcessDirectory 遍历所有文件并调用 ProcessFile 方法。之后,它还会遍历子目录并调用自身。

    '*********************************************

    '

    ' ProcessDirectory Method

    '

    ' Files in the directories are searched

    '

    '********************************************

    Private Sub ProcessDirectory(ByVal targetDirectory As String)
      Dim fileEntries As String()
      Dim subdirectoryEntries As String()
      Dim filePath As String
      Dim subdirectory As String

      fileEntries = Directory.GetFiles(targetDirectory)

      ' Process the list of files found in the directory


      For Each filePath In fileEntries
        m_totalFilesSearched += 1
        ProcessFile(filePath)
      Next filePath

      subdirectoryEntries = Directory.GetDirectories(targetDirectory)
      ' Recurse into subdirectories of this directory


      For Each subdirectory In subdirectoryEntries

        'Check to make sure the folder about to be searched 

        'is not a barred folder if it is then don't search

        If Not InStr(1, Searchs.Site.BarredFolders, _ 
         Path.GetFileName(subdirectory), vbTextCompare) > 0 Then
          'Call the search sub prcedure to search the web site

          ProcessDirectory(subdirectory)
        End If

      Next subdirectory

    End Sub 'ProcessDirectory

ProcessFile 方法

ProcessFile 调用 GetInfo,它返回 Searchs.Page 对象,该对象包含特定文件的所有信息。之后,它检查 matchcount 是否大于 0,并调用 CheckFileInfo 来清理 Page 对象中存储的信息。然后,它将文件存储在 PagesDataset 中。

    '*******************************************************

    '

    ' ProcessFile Method

    '

    ' Real logic for processing found files would go here.

    '

    '*******************************************************

    Private Sub ProcessFile(ByVal FPath As String)
      Dim srchFile As Searchs.Page

      srchFile = GetInfo(FPath)
      If Not IsNothing(srchFile) Then

        srchFile.Search(m_searchWords, m_searchCriteria)
        If srchFile.MatchCount > 0 Then
          m_totalFilesFound += 1
          'Response.Write(srchFile.Contents)

          srchFile.CheckFileInfo()
          Searchs.PagesDataset.StoreFile(m_dstPages, srchFile)
        End If

      End If

    End Sub 'ProcessFile

GetInfo 方法

GetInfo 方法的主要任务是获取文件数据。它调用共享方法 Searchs.FileContent.GetFileInfo,该方法完成了大部分工作。

    '*****************************************************************

    '

    ' GetInfo Method

    '

    ' File data is picked in this method

    '

    '*****************************************************************

    Private Function GetInfo(ByVal FPath As String) As Searchs.Page

      Dim fileInform As New FileInfo(FPath)
      Dim sr As StreamReader
      Dim srchFile As New Searchs.Page()
      Dim strBldFile As New StringBuilder()
      Dim strFileURL As String 'Holds the path to the file on the site



      'Check the file extension to make sure the file 

      'is of the extension type to be searched


      If InStr(1, Searchs.Site.FilesTypesToSearch, _ 
       fileInform.Extension, vbTextCompare) > 0 Then
        'm_page.Trace.Warn("File ext.", fileInform.Extension)

        'Check to make sure the file about to be searched 

        'is not a barred file if it is don't search the file

        If Not InStr(1, Searchs.Site.BarredFiles, _ 
         Path.GetFileName(FPath), vbTextCompare) > 0 Then
          'm_page.Trace.Warn("File", FPath)


          If Not File.Exists(FPath) Then
            'm_page.Trace.Warn("Error", _

            'String.Format("{0} does not exist.", FPath))

            'Add throw excetion here

            '

            '

            Return Nothing
          End If

          Searchs.FileContent.GetFileInfo(FPath, srchFile)

          Return srchFile

        End If
      End If
      Return Nothing
    End Function

FileContent.vb

GetFileInfo 方法

此处检索页面中的数据块。通过调用 GetStaticFileContent 方法,如果文件是静态的,则从源读取文件内容。如果文件是动态的,则通过 GetDynamicFileContent 方法从服务器检索内容。标题信息从标题标签中检索,描述和关键字从 meta 标签中检索,方法是调用 GetMetaContent 方法。通过调用 Searchs.CleanHtml.Clean 方法,从 HTML 页面中剥离文件内容。

    '**********************************************

    '

    ' GetFileInfo Method

    '

    ' File data is picked in this method

    '

    '**********************************************

    Public Shared Sub GetFileInfo(ByVal FPath As String, _ 
         ByVal srchFile As Searchs.Page)
       Dim fileInform As New FileInfo(FPath)
        Dim strBldFile As New StringBuilder()
        Dim fileSize As Decimal = fileInform.Length \ 1024
      
      srchFile.Size = fileSize
      GetFilePath(FPath, srchFile)

      If InStr(1, Searchs.Site.DynamicFilesTypesToSearch, _
        fileInform.Extension, vbTextCompare) > 0 Then
        m_page.Trace.Warn("Path", String.Format("{0}/{1}", "", _
         srchFile.Path))
        GetDynamicFileContent(srchFile)
      Else
        GetStaticFileContent(FPath, srchFile)
      End If

          If Not srchFile.Contents.Equals("") Then

    
        srchFile.Contents = sr.ReadToEnd()

        'Read in the title of the file

        srchFile.Title = GetMetaContent(srchFile.Contents,_
             "<title>", "</title>")
        'm_page.Trace.Warn("Page Title", strPageTitle)


        'Read in the description meta tag of the file

        srchFile.Description = GetMetaContent(srchFile.Contents,_
           "<meta name=""description"" content=""", ",""">")
        'm_page.Trace.Warn("Page Desc", strPageDescription)

        'Read in the keywords of the file
        srchFile.Keywords = GetMetaContent(srchFile.Contents,_
          "<meta name=""keywords"" content=""", ",""">")
        'm_page.Trace.Warn("Page Keywords", strPageKeywords)


        srchFile.Contents = _
          Searchs.CleanHtml.Clean(srchFile.Contents)

        srchFile.Contents = _
          strBldFile.AppendFormat("{0} {1} {2} {3}", _
          srchFile.Contents, srchFile.Description, _
          srchFile.Keywords, srchFile.Title).ToString.Trim()
        'm_page.Trace.Warn("File Info", strBldFile.ToString) 

        End If
              End Sub
         
     '******************************************************

    '

    ' GetStaticFileContent Method

    '

    ' File Content is picked in this method

    '

    '*******************************************************

    Private Shared Sub GetStaticFileContent(_
ByVal FPath As String, ByVal srchFile As Searchs.Page)
      Dim sr As StreamReader

      If Searchs.Site.Encoding.Equals("utf-8") Then
        sr = File.OpenText(FPath)
      Else
        sr = New StreamReader(FPath, _
Encoding.GetEncoding(Searchs.Site.Encoding))
      End If

      Try
        srchFile.Contents = sr.ReadToEnd()
        sr.Close()
      Catch ex As Exception
        m_page.Trace.Warn("Error", ex.Message)
        srchFile.Contents = ex.Message
      End Try
    End Sub
    

GetDynamicFileContent

GetDynamicFileContent 根据编码分支到 GetDynamicFileContentOtherGetDynamicFileContentUTF 方法。

    '*********************************************************************

    '

    ' GetDynamicFileContent Method

    '

    ' File Content is picked in this method

    '

    '*********************************************************************

    Private Shared Sub GetDynamicFileContent(ByVal srchFile As Searchs.Page)
      Dim wcMicrosoft As System.Net.WebClient

      If Searchs.Site.Encoding.Equals("utf-8") Then
        GetDynamicFileContentUTF(srchFile)
      Else
        GetDynamicFileContentOther(srchFile)
      End If

    End Sub
        

System.Net.WebClient 提供用于通过 URI 标识的资源发送数据和接收数据的常用方法。我们使用 DownloadData 来从资源下载数据并返回一个字节数组。

以公共语言运行时为目标的应用程序使用编码将字符表示从本机字符方案(Unicode)映射到其他方案。应用程序使用解码将字符从非本机方案(非 Unicode)映射到本机方案。System.Text 命名空间提供了允许您对字符进行编码和解码的类。

    
    '****************************************************************

    '

    ' GetDynamicFileContentOther Method

    '

    ' File Content is picked in this method 

    ' according to the encoding provided

    '

    '****************************************************************

    Private Shared Sub GetDynamicFileContentOther( _
           ByVal srchFile As Searchs.Page)
      Dim wcMicrosoft As System.Net.WebClient
      Dim fileEncoding As System.Text.Encoding

      Try
        fileEncoding = System.Text.Encoding.GetEncoding(_
       Searchs.Site.Encoding)
        srchFile.Contents = fileEncoding.GetString( _
        wcMicrosoft.DownloadData(String.Format("{0}/{1}", _
      Searchs.Site.ApplicationPath, srchFile.Path)))
      Catch ex As System.Net.WebException
        m_page.Trace.Warn("Error", ex.Message)
        srchFile.Contents = ex.Message
      Catch ex As System.Exception
        m_page.Trace.Warn("Error", ex.Message)
        srchFile.Contents = ex.Message
      End Try

    End Sub

UTF8Encoding 类使用 UCS Transformation Format,8 位形式(UTF-8)对 Unicode 字符进行编码。此编码支持所有 Unicode 字符值和代理项。

    '*********************************************************************

    '

    ' GetDynamicFileContentUTF Method

    '

    ' File Content is picked in this method according to the utf-8 encoding 

    '

    '*********************************************************************

    Private Shared Sub GetDynamicFileContentUTF( _
           ByVal srchFile As Searchs.Page)
      Dim wcMicrosoft As System.Net.WebClient
      Dim objUTF8Encoding As UTF8Encoding

      Try
        wcMicrosoft = New System.Net.WebClient()
        objUTF8Encoding = New UTF8Encoding()
        srchFile.Contents = objUTF8Encoding.GetString( _
        wcMicrosoft.DownloadData(String.Format("{0}/{1}", _
     Searchs.Site.ApplicationPath, srchFile.Path)))
      Catch ex As System.Net.WebException
        m_page.Trace.Warn("Error", ex.Message)
        srchFile.Contents = ex.Message
      Catch ex As System.Exception
        m_page.Trace.Warn("Error", ex.Message)
        srchFile.Contents = ex.Message
      End Try

    End Sub
        

GetFilePath 方法

GetFilePath 方法将本地文件夹路径转换为反映网站 URL。

    '*****************************************

    '

    ' GetFilePath Method

    '

    ' File path is modfied to be displayed 

    ' as hyperlink in this method

    '

    '*****************************************

    Private Shared Sub GetFilePath(ByVal strFileURL As String,_
                ByVal srchFile As Searchs.Page)
      'Turn the server path to the file into a URL path to the file

      strFileURL = Replace(strFileURL, m_page.Server.MapPath("./"), "")

      'Replace the NT backslash with the internet 

      'forward slash in the URL to the file


      strFileURL = Replace(strFileURL, "\", "/")

      'Encode the file name and path into the URL code method

      strFileURL = m_page.Server.UrlEncode(strFileURL)

      'Just incase it's encoded any backslashes
      strFileURL = Replace(strFileURL.Trim(), _
               "%2f", "/", vbTextCompare)
      srchFile.Path = strFileURL

      m_page.Trace.Warn("Url", srchFile.Path)
    End Sub

GetMetaContent 方法

GetMetaContent 方法使用正则表达式来剥离标签并获取所需信息。

    '************************************************

    '

    ' GetMetaContent Method

    '

    ' Metacontent is stripped in this method

    '

    '************************************************

    Private Shared Function GetMetaContent(ByVal strFile As String, _
     ByVal strMetaStart As String, ByVal strMetaEnd As String) As String
      'List the text between the title tags:

      Dim regexp As Regex
      Dim strMeta As String
      Dim strPattern As String
      Dim strInPattern As String

      'If no description or keywords are found then you may be 

      'using http-equiv= instead of name= in your meta tags

      If InStr(1, LCase(strFile), strMetaStart, 1) = 0 _
       And InStr(strMetaStart, "name=") Then
        'Swap name= for http-equiv= 

        strMetaStart = Replace(strMetaStart, "name=", "http-equiv=")
      End If

      'Build Pattern

      strInPattern = "((.|\n)*?)"
      strPattern = String.Format("{0}{1}{2}", _
       strMetaStart, strInPattern, strMetaEnd)
      regexp = New Regex(strPattern, RegexOptions.IgnoreCase)

      'Match Pattern

      strMeta = regexp.Match(strFile).ToString

      'Build Pattern

      strInPattern = "(.*?)"
      strPattern = String.Format("{0}{1}{2}", _
       strMetaStart, strInPattern, strMetaEnd)

      'Get Pattern content

      strMeta = regexp.Replace(strMeta, strPattern,_
                "$1", RegexOptions.IgnoreCase)

      Return strMeta
    End Function

PagesDataset.vb

此类用于创建和构建 DataSet。它包含两个方法StoreFileCreate 方法创建用于存储搜索结果的 DataSet,而 Storefile 负责将记录添加到 DataSet 中的 DataTable

      
    '*******************************************************

    '

    ' Create Method - Shared method

    '

    ' Creates a datset for the pages and returns the result

    '

    '********************************************************

    Public Shared Function Create() As DataSet
      'Objects are defined

      Dim pgDataSet As New DataSet()
      Dim keys(1) As DataColumn

      'Table is created and added to table collection

      pgDataSet.Tables.Add(New DataTable("Pages"))

      'Schema of table is defined

      pgDataSet.Tables("Pages").Columns.Add("PageId", _
         System.Type.GetType("System.Int32"))
      pgDataSet.Tables("Pages").Columns.Add("Title",_
         System.Type.GetType("System.String"))
      pgDataSet.Tables("Pages").Columns.Add("Description", _
         System.Type.GetType("System.String"))
      pgDataSet.Tables("Pages").Columns.Add("Path", _
         System.Type.GetType("System.String"))
      pgDataSet.Tables("Pages").Columns.Add("MatchCount", _
         System.Type.GetType("System.Int32"))
            pgDataSet.Tables("Pages").Columns.Add("Size", _
         System.Type.GetType("System.Decimal"))

      'PageId is defined as indentity

      pgDataSet.Tables("Pages").Columns("PageID").AutoIncrement = True
      pgDataSet.Tables("Pages").Columns("PageID").AutoIncrementSeed = 1

      'PageId is defined as the primary key

      keys(0) = pgDataSet.Tables("Pages").Columns("PageId")
      pgDataSet.Tables("Pages").PrimaryKey = keys

      Return pgDataSet
    End Function

    '********************************************************

    '

    ' StoreFile Method - Shared method

    '

    ' Creates a datset for the pages and returns the result

    '

    '********************************************************

    Public Shared Sub StoreFile(ByVal dstPgs As DataSet,_
                  ByVal srchPg As Searchs.Page)
      'Objects are defined

      Dim pageRow As DataRow

      'New row is created

      pageRow = dstPgs.Tables("Pages").NewRow()

      'Data is added

      pageRow("Title") = srchPg.Title
      pageRow("Description") = srchPg.Description
      pageRow("Path") = srchPg.Path
      pageRow("MatchCount") = srchPg.MatchCount
             pageRow("Size") = srchPg.Size

      'Row is added to the dataset

      dstPgs.Tables("Pages").Rows.Add(pageRow)
    End Sub

CleanHtml.vb

CleanHtml 类包含一个公共共享方法,该方法使用正则表达式清理 HTML 内容。

    '*****************************************************

    '

    ' CleanFileContent Method

    '

    ' Subroutine to the clean the file of html content

    '

    '*****************************************************

    Public Shared Function Clean(ByVal Contents As String) As String
      Dim regexp As Regex
      Dim strPattern As String

      strPattern = ""
      regexp = New Regex(strPattern, RegexOptions.IgnoreCase)

      Contents = regexp.Replace(Contents, _
       "<(select|option|script|style|title)(.*?)" & _
       ">((.|\n)*?)</(SELECT|OPTION|SCRIPT|STYLE|TITLE)>",_
       " ", RegexOptions.IgnoreCase)

      Contents = regexp.Replace(Contents, "&(nbsp|quot|copy);", "")

      'Contents = regexp.Replace(Contents, "<[^>]*>", "")


      Contents = regexp.Replace(Contents, "<([\s\S])+?>",_
        " ", RegexOptions.IgnoreCase).Replace(" ", " ")

      'Contents = regexp.Replace(Contents, "<[^<>]+>",_

        " ", RegexOptions.IgnoreCase)

      'Contents = regexp.Replace("(<(\w+)[^>]*?>(.*?)</\1>", "$1")


      Contents = regexp.Replace(Contents, "\W", " ")

      'Trace.Warn("File Contents", Contents)


      Return Contents

    End Function

Site.vb

Site 类包含共享属性,用于存储整个站点的配置。这些属性通过 ConfigurationSettings.AppSettingsweb.config 文件获取其值。

以下是站点类的属性

FilesTypesToSearch 返回您想要搜索的文件类型
DynamicFilesTypesToSearch 返回要搜索的动态文件
BarredFolders 返回受限制的文件夹
EnglishLanguage 返回一个 Boolean 值,表示语言是否为英语。
编码 返回站点的编码
BarredFiles 返回受限制的文件
ApplicationPath 分配并返回应用程序的路径
    '*************************************************

    '

    ' FilesTypesToSearch ReadOnly Property

    '

    ' Retrieve FilesTypesToSearch of the site

    '

    '*************************************************

    Public Shared ReadOnly Property FilesTypesToSearch() As String
      Get
        Return ConfigurationSettings.AppSettings(
        "FilesTypesToSearch")
      End Get
    End Property
    
    '*************************************************

    '

    ' DynamicFilesTypesToSearch ReadOnly Property

    '

    ' Retrieve FilesTypesToSearch of the site

    '

    '*************************************************

    Public Shared ReadOnly Property DynamicFilesTypesToSearch() As String
      Get
        Return ConfigurationSettings.AppSettings(_
         "DynamicFilesTypesToSearch")
      End Get
    End Property
    '*************************************************

    '

    ' BarredFolders ReadOnly Property

    '

    ' Retrieve BarredFolders of the site

    '

    '*************************************************

    Public Shared ReadOnly Property BarredFolders() As String
      Get
        Return ConfigurationSettings.AppSettings("BarredFolders")
      End Get
    End Property

    '*************************************************

    '

    ' BarredFiles ReadOnly Property

    '

    ' Retrieve BarredFiles of the site

    '

    '*************************************************

    Public Shared ReadOnly Property BarredFiles() As String
      Get
        Return ConfigurationSettings.AppSettings("BarredFiles")
      End Get
    End Property

    '*************************************************

    '

    ' EnglishLanguage Property

    '

    ' Retrieve EnglishLanguage of the site

    '

    '*************************************************

    Public Shared ReadOnly Property EnglishLanguage() As String
      Get
        Return ConfigurationSettings.AppSettings("EnglishLanguage")
      End Get
    End Property
     
    '*********************************************************************

    '

    ' Encoding Property

    '

    ' Retreive Encoding of the site

    '

    '*********************************************************************

    Public Shared ReadOnly Property Encoding() As String
      Get
        Return ConfigurationSettings.AppSettings("Encoding")
      End Get
    End Property
        
    '**********************************************************

    '

    ' ApplicationPath Property

    '

    'Assign and retrieve ApplicationPath of the site

    '

    '**********************************************************

    Public Property ApplicationPath() As String
      Get
        Return m_ApplicationPath
      End Get
      Set(ByVal Value As String)
        m_ApplicationPath = Value
      End Set
    End Property
        

Web.config

ASP.NET 配置系统具有可扩展的基础结构,它允许您在 ASP.NET 应用程序首次部署时定义配置设置,以便您可以随时添加或修改配置设置,而对运行中的 Web 应用程序和服务器的影响最小。多个名为 Web.config 的配置文件可以出现在 ASP.NET Web 应用程序服务器上的多个目录中。每个 Web.config 文件都将其配置设置应用于其自身的目录及其下的所有子目录。如前所述,站点配置可以在 web.config 文件中指定。

<appSettings>

 <!-- Place the names of the files types you want searching 
 in the following line separated by commas -->

 <add key="FilesTypesToSearch" value=".htm,.html,.asp,.shtml,.aspx" 
 /> 
    <!-- Place the names of the dynamic files types you want 
 searching in the following line separated by commas -->
     
 <add key="DynamicFilesTypesToSearch" value=".asp,.shtml,.aspx" /> 

 <!-- Place the names of the folders you don't 
 want searched in the following line separated by commas--> 

 <add key="BarredFolders" 
  value="aspnet_client,_private,_vti_cnf,_vti_log,_vti_pvt,
_vti_script,_vti_txt,cgi_bin,_bin,bin,_notes,images,scripts" 
 />

 <!-- Place the names of the files you don't want searched in the 
 following line separated by commas include the file extension--> 

 <add key="BarredFiles" 
  value="localstart.asp,iisstart.asp,AssemblyInfo.vb,
          Global.asax,Global.asax.vb,SiteSearch.aspx" 
 />

 <!-- Set this boolean to False if you are not using 
 an English language web site--> 

 <add key="EnglishLanguage" value="True" /> 

 <!-- Set this to the Encoding of the web site-->   
 <add key="Encoding" value="utf-8" />  

 </appSettings>

如何集成

该应用程序已在根目录中与 Web 窗体 SiteSearch.aspx 一起进行了测试。所以我的建议是您也这样做。之后,您可以尝试将其移动到任何子文件夹。我所有的类都放在了 components 文件夹中。您可以将它们移动到任何您喜欢的文件夹。

注意

  1. 对于那些没有 Visual Studio .Net 的用户
    1. 从链接“下载最新演示项目(无需 Visual studio.net)”下载
    2. 将 SearchDotnet.dll 放在根目录的 bin 文件夹中。
    3. 将 SiteSearch.aspx 和 web.config 放在根目录中。
  2. 使用 XML 版本
    1. 从链接“下载演示项目(读写 XML,VB.net)”下载
    2. 项目包含以下文件。
      1. AdminSearch.aspx 用于向文件写入 XML。
      2. SiteSearch.aspx 用于搜索文件。
      3. 我所有的类都放在了 components 文件夹中。

错误

当应用程序放在根目录下时,您可能会遇到以下错误。远程服务器返回了一个错误: (401) 未授权。

或者

远程服务器返回了一个错误: (500) 内部服务器错误。

此错误是由于

  1. 如果服务器返回 (401) 未授权,则应用程序由于权限不足而无法读取文件。
  2. 如果服务器返回 (500) 内部服务器错误,则它试图读取的页面返回了一个错误。该应用程序试图读取的页面要么有错误,要么需要参数,因此返回错误。

按照以下步骤纠正错误

  1. 在 Web.config 中,确保 BarredFolders 列表是全面的
    aspnet_client,_private,_vti_cnf, _vti_log,_vti_pvt,_vti_script,_vti_txt, cgi_bin,_bin,bin,_notes,images,scripts
  2. 确保 BarredFiles 列表是全面的,并且包含 localstart.asp,iisstart.asp

全球化

搜索引擎模块可以轻松进行本地化。为此,我们将学习如何将其转换为中文。

Web.config

XML 声明必须作为文档的第一行出现,前面没有任何其他内容,包括空格。

文档映射中的 XML 声明包含以下内容:<?xml version="1.0" encoding="Your Encoding" ?>。默认情况下,Visual Studio 使用 utf-8 编码,需要将其更改为您想要的编码。在这里,我们将更改为 gb2312。因此,XML 声明需要修改如下。

English

<?xml version="1.0" encoding="utf-8" ?>

Chinese

<?xml version="1.0" encoding="gb2312" ?>

requestEncodingresponseEncoding 指定了每个传入请求和传出响应的假定编码。默认编码是 UTF-8,在 .NET Framework 安装时创建的 Machine.config 文件中的 <globalization> 标记中指定。如果在 Machine.config 或 Web.config 文件中未指定编码,则编码默认为计算机的区域选项区域设置。我们需要更改 requestEncodingresponseEncoding 来反映编码更改。

English

<globalization requestEncoding="utf-8" responseEncoding="utf-8" />

Chinese

<globalization requestEncoding="gb2312" responseEncoding="gb2312" />

为了避免在编码更改时构建代码,我们需要将编码键添加到 appsettings

<!-- Set this to the Encoding of the web site-->   
<add key="Encoding" value="gb2312" />  

还将 English Language 键更改为 false。

 <!-- Set this boolean to False if you are not using 
 an English language web site--> 

 <add key="EnglishLanguage" value="True" /> 

SiteSearch.aspx

最后但并非最不重要的一点是,必须在页面指令中添加 codepage 属性。

English

<%@ Page Language="vb" Trace="False" AutoEventWireup="false"
 Codebehind="SiteSearch.aspx.vb" 
 Inherits="SearchDotnet.SiteSearch" debug="false" %>

Chinese

<%@ Page Language="vb" Trace="False" AutoEventWireup="false" 
 Codebehind="SiteSearch.aspx.vb" Inherits="SearchDotnet.SiteSearch" 
 debug="false" codePage="936" %>

增强代码

该应用程序适用于小型网站。对于大型网站,代码可以进一步增强。实际上,您需要定期将数据写入数据库(例如 XML 文件),然后再从中读取。我将提供一些实现此目的的技巧。(我现在包含了一个读取和写入 XML 的演示项目。)

(1) 在我的代码中,我使用正则表达式搜索和过滤数据。您需要使用以下方法将整个数据(未过滤的数据)写入 XML 文件。

   Private Shared Sub WriteXmlToFile(ByVal thisDataSet As DataSet)
      If thisDataSet Is Nothing Then
        Return
      End If
      
      thisDataSet.WriteXml(XMLFile)
    End Sub

(2) 之后,您需要从文件中读取 XML,将其保存到共享数据集中,例如 Searchs.Site.PageDataset.Tables("Pages").

    Private Shared Function ReadXmlFromFile() As DataSet
      ' Create a new DataSet.

      Dim newDataSet As New DataSet("New DataSet")

      ' Read the XML document back in. 

      ' Create new FileStream to read schema with.

      Dim fsReadXml As New System.IO.FileStream(XMLFile, 
        System.IO.FileMode.Open)

      ' Create an XmlTextReader to read the file.

      Dim myXmlReader As New System.Xml.XmlTextReader(fsReadXml)

      ' Read the XML document into the DataSet.

      newDataSet.ReadXml(myXmlReader)

      ' Close the XmlTextReader

      myXmlReader.Close()
      Return newDataSet

    End Function

(3) 对于每个搜索,您以后需要使用 PageDataset.Tables 的 Select 方法根据搜索结果进行过滤。Filter dataset 可能看起来像这样。FillDataset 方法包含创建并向数据库添加搜索结果(DataRow 数组)的逻辑。

    Private Sub FiterPagesDatset()
      Dim strExpr As String
      Dim foundRows As DataRow()

      Dim Field() As String = {
       "Title", "Description", "Keywords", "Contents"}

      strExpr = SomeFunction 'Your function to build the query.


     
      foundRows = Searchs.Site.PageDataset.Tables(
        "Pages").Select(strExpr)
      FillDataset(foundRows)
    End Sub

(4) 将过滤后的结果存储在另一个数据集中,并使用它来显示结果。

关注点

当我从事该项目时,问题是如何显示结果。DataGrid 是我的选择,因为我们可以利用它许多其他列表控件所不具备的功能。一旦解决了我的问题,下一个问题是如何将内容传递给 DataGridDataSet 是唯一的替代方案。在我进一步工作,在将信息存储到 DataSet 之前,我不得不来回处理页面的大量信息。我决定使用站点对象来存储信息。

其中一位作者提出了以下最佳实践

  1. 类应该很小,大约有半打方法和属性。
  2. 方法应该简短,同样大约半打行。

在仔细分析代码并牢记最佳实践后,我将代码重新设计为现在的样子。

历史

  • 修改代码以读取动态页面。
  • 增强代码以支持全球化。
  • 我还包含了一个读取和写入 XML 的演示项目
© . All rights reserved.