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

Peer Graph - 搜索标准帮助程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.17/5 (2投票s)

2006年1月16日

7分钟阅读

viewsIcon

37040

downloadIcon

510

使用Microsoft的Peer-to-Peer技术将表达式转换为Peer Graph的XML搜索条件。

背景

Microsoft的Peer-to-Peer Graphing技术为Windows的点对点应用程序提供了稳定、可靠和健壮的通信基础设施。对等节点使用Peer Name Resolution Protocol (PNRP - 一个无服务器DNS) 在图中注册和发现其他对等节点。图是连接对等节点、服务和资源的基础。对等节点可以是用户交互式应用程序、服务或资源。Graphing允许数据在对等节点之间高效可靠地传递。

Microsoft的整个Peer-to-Peer技术通过最新的Platform SDK暴露为C/C++ API调用。然而,本文中的代码展示了如何使用C#中的.NET托管代码来调用这些API。

引言

本文介绍了一个辅助类,该类允许您以更类似SQL的方式表达搜索条件。正如我们在上一篇文章中学到的,搜索对等节点记录中的元数据使用简单的XML搜索条件。例如

<peersearch>
 <and>
   <and>
    <clause attrib="creationtime" type="date" 
      compare="greaterorequal">2005-07-01T00:00:00.0000000-06:00
    </clause>
    <clause attrib="creationtime" type="date" 
      compare="lessorequal">2005-09-01T00:00:00.0000000-06:00
    </clause>
   </and>
   <clause attrib="length" type="int" 
         compare="greater">15000</clause>
 </and>
</peersearch>

本文介绍的辅助类提供了一个方法,用于将类似SQL的表达式转换为所需的XML搜索条件格式。以下表达式转换为上面的XML片段

creationtime between '2005-07-01' and '2005-09-01' and length > 15000

可以说,这样更容易阅读。

语法

首先,我使用GOLD (Grammar Oriented Language Development) 工具包来表示我的语法。如果您想更好地理解这项技术,我建议您阅读几个月前我写的关于GOLD的入门文章

我创建了一个简单的语法来匹配搜索条件的功能。让我们从表示与XML搜索条件类似的表达式所需的规则开始。

<Search List> ::= <Search List> AND <Comparison>
  | <Search List> OR <Comparison>
  | <Comparison>

搜索包含比较。它可以是单个比较,如a=1,也可以是多个由AND或OR分隔的比较。

<Comparison> ::= Identifier LIKE StringLiteral
  | Identifier BETWEEN <Value> AND <Value>
  | Identifier IN '(' <Value List> ')'
  | Identifier '>' <Value> 
  | Identifier '<' <Value> 
  | Identifier '<=' <Value> 
  | Identifier '>=' <Value>
  | Identifier '=' <Value> !Equal
  | Identifier '<>' <Value> !Not equal
  | Identifier '!=' <Value> !Not equal
  | '(' <Search List> ')'

有几种类型的比较。

  • LIKE将值与字符串模式进行比较,例如,name LIKE 'James *'
  • BETWEEN确定值是否在范围内,例如,created BETWEEN '2005-07-01' and '2005-08-01'
  • IN将值与一组字面值进行比较,例如,grade IN ('A', 'B', 'C')
  • 还有一些典型的比较,如a=1, a>2, and b!=5。
  • 比较也可以使用括号进行分组,以确保优先级。

接下来是值。值是字面量,如字符串或数字。数字可以是正数或负数,可以是整数或浮点数。注意科学计数法 (1.0e+3) 也得到支持。

<Unary> ::= '-' | '+' |

<Value> ::= <Unary> IntegerLiteral
  | <Unary> RealLiteral
  | StringLiteral
  | CAST '(' <Value> AS <Type> ')'

值也可以通过CAST显式地从一种数据类型转换为另一种数据类型。请注意,日期会隐式地从字符串转换;因此,永远不需要CAST('1900-01-01' as string)。

最后,值得一提的是,BETWEEN、IN、CAST、LIKE、AND和OR这些标记*不*区分大小写。

PeerSearchCriteria类

此类实现了两个共享(静态)方法,称为ExpressionToXML。第一个方法接受两个参数;要解析的表达式和一个布尔值,指示返回的XML是否应该格式化。第二个方法只接受表达式参数,并返回未格式化的XML。

Public Shared Function ExpressionToXML(ByVal _
           Expression As String, ByVal Indent As Boolean) As String
  If (Expression.Trim = String.Empty) Then
      Return "<peersearch/>"

  If _grammar Is Nothing Then
    Dim exec As [Assembly] = [Assembly].GetExecutingAssembly
    Dim stream As stream = _
       exec.GetManifestResourceStream("RecordSearch.cgt")
    _grammar = New Grammar(New BinaryReader(stream))
  End If

  Dim parser As SearchCriteriaParser = _
      New SearchCriteriaParser(Expression, _grammar)
  If parser.DoParse(New StringReader(Expression)) = _
                              ParseMessage.Accept Then
    Dim xw As New System.Xml.XmlTextWriter(New MemoryStream, _
                               System.Text.Encoding.Unicode)
    If Indent Then xw.Formatting = Xml.Formatting.Indented
    xw.WriteStartElement("peersearch")
    CType(parser.CurrentReduction.Tag, _
                 IASTNode).WriteToXML(xw)
    xw.WriteEndElement()
    xw.Flush()
    xw.BaseStream.Position = 0
    ExpressionToXML = New StreamReader(xw.BaseStream).ReadToEnd()
  Else
    Throw New SearchCritieraException(parser.Message)
  End If
End Function

Public Shared Function ExpressionToXML(ByVal _
              Expression As String) As String
  Return ExpressionToXML(Expression, False)
End Function

ExpressionToXML方法首先检查空表达式字符串。然后,如果这是该方法第一次被调用,它将使用嵌入资源中的编译后的二进制语法版本来初始化规则引擎。然后创建解析器并解析表达式。这会创建一个抽象语法树 (AST)。AST是一个对象树,其中每个对象(节点)代表语法中规则的标记。如果发生任何错误,将抛出一个SearchCriteriaException。否则,将创建一个XmlTextWriter,并从根节点开始遍历AST。AST中的每个节点都贡献其代表规则的XML描述。遍历完整个树后,将构建完整的XML片段。XML被提取并作为字符串返回。

AST中存储三种类型的节点;搜索列表、比较和值。搜索列表和比较节点实现了IASTNode接口,该接口有一个生成节点代表的规则的等效XML片段的单一方法。例如,下面的SearchList类代表了语法中的<Search List>规则。

Class SearchList
  Implements IASTNode

  Friend List As New ArrayList '(Of SearchItem)
  Public Sub New(ByVal item As SearchItem)
    List.Add(item)
  End Sub

  Public Sub Add(ByVal item As SearchItem)
    List.Add(item)
  End Sub

  Public Sub WriteToXML(ByVal xw As System.Xml.XmlTextWriter) _
             Implements IASTNode.WriteToXML
    Dim item As SearchItem = CType(List(0), SearchItem)
    If List.Count = 1 Then
      item.WriteToXML(xw)
    Else
      ' operation is always the second item in the list
      Dim Operation As String = CType(List(1), _
                                SearchItem).Operation
      xw.WriteStartElement(Operation)

      Dim i As Integer = 0
      Do
        item.WriteToXML(xw)
        i += 1
        item = CType(List(i), SearchItem)
      Loop While i < List.Count - 1

      ' don't forget the last item
      item.WriteToXML(xw)
      xw.WriteEndElement()
    End If
  End Sub
End Class

搜索列表包含一个SearchItem列表。SearchItem是比较加上一个操作(AND、OR或空)。WriteXML方法首先检查列表中SearchItem的数量。如果只有一个(例如,name = 'Bill'),它会调用SearchItemWriteXML方法。否则,有一个列表(例如,name = 'Bill' AND age > 50)。根据搜索记录的模式规则,必须首先生成<and><or>标记。在此标记内,WriteXML方法循环遍历列表中的SearchItem。这可能会导致递归,但这已被正确处理。最后,处理列表中的最后一项并写入结束标记。

比较要简单得多。所有比较都派生自一个名为Comparison的抽象基类。

MustInherit Class Comparison
  Implements IASTNode

  Public Const EQUAL_COMPARE As String = "equal"
  Public Const GREATER_COMPARE As String = "greater"
  Public Const LESS_COMPARE As String = "less"
  Public Const NOTEQUAL_COMPARE As String = "notequal"
  Public Const GREATEROREQUAL_COMPARE As String = "greaterorequal"
  Public Const LESSOREQUAL_COMPARE As String = "lessorequal"

  Public MustOverride Sub WriteToXML(ByVal xw As _
         System.Xml.XmlTextWriter) Implements IASTNode.WriteToXML

  Protected Sub WriteClause(ByVal xw As System.Xml.XmlTextWriter, _
            ByVal Attrib As String, ByVal Type As String, _
            ByVal Compare As String, ByVal Value As String)
    xw.WriteStartElement("clause")
    xw.WriteAttributeString("attrib", Attrib)
    xw.WriteAttributeString("type", Type)
    xw.WriteAttributeString("compare", Compare)
    xw.WriteString(Value)
    xw.WriteEndElement()
  End Sub
End Class

Comparison类声明了一些字符串常量,用于XML比较名称。它还提供了一个有用的方法来封装生成<clause attrib="" type="" compare=""></clause>标记。以下示例显示了BETWEEN规则的派生类。

Class BetweenComparison
  Inherits Comparison

  Friend Identifier As String
  Friend LeftBetween As Value
  Friend RightBetween As Value

  Public Sub New(ByVal Expr As String, _
         ByVal Left As Value, ByVal Right As Value)
    Identifier = Expr
    LeftBetween = Left
    RightBetween = Right
  End Sub

  Public Overrides Sub WriteToXML(ByVal xw As System.Xml.XmlTextWriter)
    xw.WriteStartElement("and")
    WriteClause(xw, Identifier, LeftBetween.DataType, _
                GREATEROREQUAL_COMPARE, LeftBetween.Value)
    WriteClause(xw, Identifier, RightBetween.DataType, _
                LESSOREQUAL_COMPARE, RightBetween.Value)
    xw.WriteEndElement()
  End Sub
End Class

同样,规则的信息被存储;在这种情况下,是标识符以及左值和右值。由于BETWEEN表示范围内的值,这会转换为<and>标记内的两个比较子句。

Value类是最简单的。所有值都派生自一个名为Value的抽象基类。

MustInherit Class Value
  Public Const INT_TYPE As String = "int"
  Public Const DATE_TYPE As String = "date"
  Public Const STRING_TYPE As String = "string"

  Public MustOverride ReadOnly Property Value() As String
  Public MustOverride ReadOnly Property DataType() As String
End Class

为XML数据类型声明了常量字符串。派生类必须覆盖方法以指示其值和数据类型。以下是代表日期的类的代码。

Class DateValue
  Inherits Value

  Private Data As Date

  Public Sub New(ByVal v As Date)
    Data = v
  End Sub

  Public Overrides ReadOnly Property DataType() As String
    Get
      Return MyBase.DATE_TYPE
    End Get
  End Property

  Public Overrides ReadOnly Property Value() As String
    Get
      Return System.Xml.XmlConvert.ToString(Data)
    End Get
  End Property
End Class

DataType属性返回日期数据类型的XML字符串。Value属性返回日期的XML值。

使用示例应用程序

示例应用程序允许您首先创建一个图(未加密的对等节点名称0.SearchSharedFiles)并设置一个初始标识。第一个实例应该使用此标识打开。它将暂停几秒钟以查找其他实例,然后开始监听。应用程序的每个后续实例都应该使用不同的标识打开图。这些实例将连接到最近的对等节点并同步。应用程序的每个实例都是一个对等节点。

在“共享”选项卡上,使用“添加文件夹”按钮选择包含文件的文件夹。选择文件夹后,文件夹中的每个文件都作为记录发布到图中。请注意,记录设置为在10分钟后过期。记录的属性被设置为FileInfo类的属性。单击列表框中的文件名以在右侧文本框中显示与记录关联的XML属性。下面的列表显示了所有操作和传入事件的诊断日志。双击以清除列表。请注意,“windows”和“system32”文件夹提供了一组良好的文件用于搜索。

在“搜索”选项卡上,输入搜索条件表达式。源代码和演示包括一个名为Search Criteria Expression Examples.txt的文件,其中包含一些方便的搜索条件表达式供您复制并粘贴到示例应用程序中。单击“搜索”按钮以发出搜索。该表达式使用辅助类快速转换为其XML格式,并在右上角的文本框中显示。然后将XML片段传递给Search方法。

private void Search_Click(object sender, System.EventArgs e)
{
  PeerSearch search = graph.CreateSearch();

  try
  {
    // convert expression to XML search criteria fragment
    textBox5.Text = 
      Peer.Utility.PeerSearchCriteria.ExpressionToXML(textBox4.Text,
      true);
    search.Xml = textBox5.Text;

    PeerRecordCollection records = search.Search();

    listBox3.Items.Clear();
    propertyGrid1.SelectedObject = null;

    // display the results in the list box
    foreach (PeerRecord record in records)
    {
      listBox3.Items.Add(new FileItem(record.DataAsString, record.ID));
    }
  }
  catch (Exception ex)
  {
    System.Windows.Forms.MessageBox.Show(ex.Message, "Search Failed");
  }
}

如果发生错误,将显示一个弹出对话框显示错误代码。否则,左下方的列表将填充与搜索条件匹配的文件名。单击此列表以在属性网格中显示记录的属性。

关注点

单元测试

示例代码包含一个包含40多个单元测试的项目,用于检查各种表达式是否都已正确转换。

限制

Microsoft提到底层PeerGraphSearchRecords API的性能效率不高。对于简单的表达式,它还不错。然而,当你开始使用更复杂的表达式和多个字符串模式时,性能会迅速变得不可用。例如,以下表达式

name in ('a*','b*','c*','d*','e*')

这要么会无限期地使用100%的CPU,要么在几分钟后导致错误。由于其他错误,我尚无法确定这在Windows Vista中是否已得到修正。

资源链接

我发现以下资源对于理解对等图形非常有帮助

结论

希望您觉得本文很有用。下一篇文章将通过讨论如何导入和导出数据库来完成Graphing系列。请继续关注以下主题的更多文章

  1. 对等名称解析 - Windows Vista 增强功能
  1. Peer Graph - 导入和导出数据库
  1. 对等组和身份
  1. 对等协作 - 附近的人
  2. 对等协作 - 端点
  3. 对等协作 - 能力
  4. 对等协作 - 在线状态
  5. 对等协作 - 邀请

如果您对其他主题有建议,请留言。

历史

  • 初始版本。
© . All rights reserved.